2327 字
12 分钟
ABC459 A-E题解
2026-10-05
无标签

[A - Hell, World!](A - Hell, World!)#

难度:入门;标签:字符串#

题目大意#

在字符串HelloWold中,删除第 XX 个字符并输出。

具体思路#

很明显,由于C++的字符串是下标是由零开始的,所以只需要循坏判断 下标是不是 X−1X - 1 就行了。

代码实现#

时间复杂度:O(N)O(N);空间复杂度:O(N)O(N)#
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define cll const long long
#define ull unsigned long long
#define db double
#define MAX LLONG_MAX
#define MIN LLONG_MIN
#define pb push_back
#define pp pop_back()
#define pr pair<ll, ll>
#define fir first
#define sec second
cll N = 1e5 + 10;
ll x;
void solve(){
string s = "HelloWorld";
cin >> x;
for(ll i = 0;i < 10;i++)
if(i != x - 1)
cout << s[i];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
ll T = 1;
// cin >> T;
while(T--)
solve();
return 0;
}

[B - 459](B - 459)#

难度:入门;标签:字符串、模拟#

题目大意#

有 NN 个字符串(后文称 Si(1≤i≤N)S_i(1\le i \le N)),取 SiS_i 的首字母,把他转变成 CiC_i。 根据以下信息转变:

  • 若 SiS_i 的首字符为 a, b, c 之一,则 Ci=2C_i = 2。
  • 若 SiS_i 的首字符为 d, e, f 之一,则 Ci=3C_i = 3。
  • 若 SiS_i 的首字符为 g, h, i 之一,则 Ci=4C_i = 4。
  • 若 SiS_i 的首字符为 j, k, l 之一,则 Ci=5C_i = 5。
  • 若 SiS_i 的首字符为 m, n, o 之一,则 Ci=6C_i = 6。
  • 若 SiS_i 的首字符为 p, q, r, s 之一,则 Ci=7C_i = 7。
  • 若 SiS_i 的首字符为 t, u, v 之一,则 Ci=8C_i = 8。
  • 若 SiS_i 的首字符为 w, x, y, z 之一,则 Ci=9C_i = 9。

最后连接 C1,C2,C3,...,CnC_1,C_2,C_3,...,C_n,输出即可。

具体思路#

这道题也很简单,只需循坏遍历每一个字符串,取首字母判断输出就行了

代码实现#

时间复杂度O(N)O(N);空间复杂度:O(N)O(N)#
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define cll const long long
#define ull unsigned long long
#define db double
#define MAX LLONG_MAX
#define MIN LLONG_MIN
#define pb push_back
#define pp pop_back()
#define pr pair<ll, ll>
#define fir first
#define sec second
cll N = 1e5 + 10;
ll n;
void solve(){
cin >> n;
for(ll i = 1;i <= n;i++){
string s;
cin >> s;
s = "0" + s;
if(s[1] == 'a' || s[1] == 'b' || s[1] == 'c')
cout << 2;
else if(s[1] == 'd' || s[1] == 'e' || s[1] == 'f')
cout << 3;
else if(s[1] == 'g' || s[1] == 'h' || s[1] == 'i')
cout << 4;
else if(s[1] == 'j' || s[1] == 'k' || s[1] == 'l')
cout << 5;
else if(s[1] == 'm' || s[1] == 'n' || s[1] == 'o')
cout << 6;
else if(s[1] == 'p' || s[1] == 'q' || s[1] == 'r' || s[1] == 's')
cout << 7;
else if(s[1] == 't' || s[1] == 'u' || s[1] == 'v')
cout << 8;
else
cout << 9;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
ll T = 1;
// cin >> T;
while(T--)
solve();
return 0;
}

[C - Drop Blocks](C - Drop Blocks)#

难度:普及/提高-;标签:模拟#

题目大意#

给你 NN 个单元格,最开始每个单元格都没有积木。 一共有两种操作:

  • 1 x:将一个图块放入第 XX 个单元格中。如果每个单元格都有1个积木,则让所有单元格的数量减一。
  • 2 y:查询所有单元格中积木数大于 YY 个的单元格数量并输出。

具体思路#

这道题如果用数组去存每一个单元格中积木数量,那么时间复杂度为 O(QN)O(QN) (因为每个操作都需要 O(N)去做)。 由于 1≤N,Q≤3×1051 \le N,Q \le 3 \times 10^5,肯定会超时,所以我们引入一个新的STL容器哈希表,这里我用的是unordered_map,不知道的看这里。 这里我为了更快处理操作1,引入了懒标记(就是先不做操作,等要用的时候再更新)。

代码实现#

时间复杂度:≈O(Q)\approx O(Q);空间复杂度:O(N)O(N)#
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define cll const long long
#define ull unsigned long long
#define db double
#define MAX LLONG_MAX
#define MIN LLONG_MIN
#define pb push_back
#define pp pop_back()
#define pr pair<ll, ll>
#define fir first
#define sec second
cll N = 3e5 + 10;
ll n, q, cha, a[N];
unordered_map<ll, ll>sum;
void solve(){
cin >> n >> q;
sum[0] = n;
while(q--){
ll op;
cin >> op;
if(op == 1){
ll x;
cin >> x;
sum[a[x]]--;
if(!sum[a[x]])
sum.erase(a[x]);
a[x]++;
sum[a[x]]++;
if(!sum.count(cha))
cha++;
}else{
ll y;
cin >> y;
ll ans = 0;
for(auto &[num, cnt] : sum)
if(num - cha >= y)
ans += cnt;
cout << ans << "\n";
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
ll T = 1;
// cin >> T;
while(T--)
solve();
return 0;
}

这时候有人就要问了,为什么操作2没超时呢。 我们来进行《一点点》数学推导。 首先由于 1≤Q≤3×1051 \le Q \le 3 \times 10^5。 我要们最大限度利用数字,所以公式为 0+1+2+3+4+...+X=Q=X(X+1)20 + 1 + 2 + 3 + 4 +...+X = Q = \frac {X(X+1)}{2} 如果 Q=3×105Q = 3 \times 10^5。 则列出一元二次方程: X2+X−6×105=0X^2 + X - 6 \times 10^5 = 0 Δ=b2−4ac=12+2.4×106>0\Delta = b^2 - 4ac = 1^2 + 2.4 \times 10^6 > 0 所有该方程有解并有两个根,根据求根公式,得: −b±Δ2a=−1±24000012\frac{-b \pm \sqrt{\Delta}}{2a} = \frac{-1 \pm \sqrt{2400001}}{2} 则 x1≈764,x2≈−775x_1 \approx 764,x_2 \approx -775 由于积木数大于等于0,则保留 x1x_1,所以哈希表内最多有765个数字。 则操作2需要的时间复杂度为 O(765)(因为前面操作1用掉了操作数)O(765)(因为前面操作1用掉了操作数) 所以不用担心超时。

[D - Adjacent Distinct String](D - Adjacent Distinct String)#

难度:普及/提高-;标签:字符串、贪心#

题目大意#

给你一个字符串,在字符串的全排列里找出一种方法使得 Si≠Si+1(1≤i<N)S_i \ne S_{i + 1}(1 \le i < N)。

具体思路#

首先我们可以想到一种构造方式为xyxy...xyx,也就是我们拿最多的和第二多的进行排列。如果除了最多的字符没有其他字符了,那就证明不合法。我们可以用priority_queue(优先队列)来实时更新。

代码实现#

时间复杂度:≈O(N)\approx O(N);空间复杂度:O(N)O(N)#
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define cll const long long
#define ull unsigned long long
#define db double
#define MAX LLONG_MAX
#define MIN LLONG_MIN
#define pb push_back
#define pp pop_back()
#define pr pair<ll, ll>
#define fir first
#define sec second
cll N = 3e5 + 10;
string s;
void solve(){
cin >> s;
string ans;
ll sum[30];
memset(sum, 0, sizeof sum);
for(char &e : s)
sum[e - 'a']++;
priority_queue<pr>q;
for(ll i = 0;i < 26;i++)
if(sum[i] != 0)
q.push({sum[i], i});
while(q.size()){
auto [num1, c1] = q.top();
q.pop();
if(num1 == 1 || ans.back() == char(c1 + 'a')){
ans.pb(char(c1 + 'a'));
continue;
}
if(!q.size()){
cout << "No\n";
return;
}
auto [num2, c2] = q.top();
q.pop();
ans.pb(char(c1 + 'a'));
ans.pb(char(c2 + 'a'));
q.push({num1 - 1, c1});
if(num2 > 1)
q.push({num2 - 1, c2});
}
cout << "Yes\n";
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
ll T = 1;
cin >> T;
while(T--)
solve();
return 0;
}

[E - Select from Subtrees](E - Select from Subtrees)#

难度:普及+/提高;标签:树形DP、拓扑排序、组合数学#

题目大意#

给你一颗根为1的树,第 ii 个节点的父亲是 PiP_i,第 ii 个节点上有 CiC_i 个糖果,第 ii只松鼠要拿 DiD_i 颗糖果,松鼠取的糖果要从是他的子树拿,且第 ii 只松鼠在第 ii 个节点上,问你松鼠选择糖果的方案数对 998244353998244353 取模是多少。

具体思路#

根据题面我们就知道第 ii 松鼠要从 XX 颗糖果中取 DiD_i 颗,很明显这就是组合数学中的组合数,公式为:Cnk=n!k!(n−k)!C_n^k = \frac{n!}{k!(n - k)!}。

由于题目中要取模,且998244353为质数,则可以使用逆元的知识,公式为 ab≡a×bp−2(mod  p)\frac{a}{b} \equiv a \times b^{p-2} (\mod p),则公式可化简为:n!k!(n−k)!≡n!×(k!(n−k)!)p−2(mod  p)\frac{n!}{k!(n - k)!} \equiv n! \times (k!(n-k)!)^{p-2}(\mod p)。

我们可以设 factifact_i 为 i!i!,inviinv_i 为 (i!)−1(i!)^{-1}(逆元)。很明显根据 n!=n×(n−1)!n! = n \times (n - 1)!,facti=i∗facti−1fact_i = i * fact_{i-1}。

那 inviinv_i 怎么求呢?我们可以根据逆元的性质 a≡b×c(mod  p)a \equiv b \times c (\mod p) 则 a−1≡b−1×c−1(mod  p)a^{-1} \equiv b^{-1} \times c^{-1}(\mod p),得 (n!)−1≡n−1×[(n−1)!]−1(mod  p)(n!)^{-1} \equiv n^{-1} \times [(n-1)!]^{-1}(\mod p)。我们用 invinv 数组表示,就是 invi≡i−1×invi−1inv_i \equiv i^{-1} \times inv_{i - 1}。再根据逆元的性质:n×n−1≡1(mod  p)n \times n^{-1} \equiv 1(\mod p),我们在两边同时乘上 ii,得 invi×i≡i−1×i×invi−1(mod  p)inv_i \times i \equiv i^{-1} \times i \times inv_{i-1}(\mod p)。也就是 invi−1≡invi×i(mod  p)inv_{i-1} \equiv inv_i \times i(\mod p) 。

然后我们该如何遍历呢?由于我的注意力惊人,发现在叶子节点的松鼠,根本不受干扰,然后他的父亲就等于他的孩子剩余糖果和加上自己的所在节点的糖果,进行组合数计算就行了。这很像拓扑排序的思想。

然后这道题就写完了

代码实现#

时间复杂度:O(N)O(N);空间复杂度:O(N)O(N)#
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define cll const long long
#define ull unsigned long long
#define db double
#define MAX LLONG_MAX
#define MIN LLONG_MIN
#define pb push_back
#define pp pop_back()
#define pr pair<ll, ll>
#define fir first
#define sec second
cll N = 3e6 + 10, M = 2e5 + 10, mod = 998244353;
ll n, p[M], deg[M], c[M], d[M], ans = 1, num[M], deg2[M];
vector<ll> fact(N), inv(N);
ll qpow(ll a, ll b) {
ll res = 1;
while (b > 0) {
if (b % 2 == 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
void init() {
fact[0] = 1;
for (ll i = 1; i < N; i++)
fact[i] = fact[i - 1] * i % mod;
inv[N-1] = qpow(fact[N - 1], mod - 2);
for (ll i = N-2; i >= 0; i--)
inv[i] = inv[i + 1] * (i + 1) % mod;
}
ll C(ll n, ll k){
if (k < 0 || k > n) return 0;
ll res = 1;
for (ll i = 0; i < k; i++)
res = res * ((n - i) % mod) % mod;
return res * inv[k] % mod;
}
void solve(){
cin >> n;
for(ll i = 2;i <= n;i++){
cin >> p[i];
deg[p[i]]++;
}
for(ll i = 1;i <= n;i++)
cin >> c[i], num[i] = c[i];
for(ll i = 1;i <= n;i++)
cin >> d[i];
queue<ll>q;
for(ll i = 1;i <= n;i++){
if(deg[i] == 0)
q.push(i);
}
while(q.size()){
ll u = q.front();
q.pop();
if(C(num[u], d[u]) == 0){
cout << 0;
return;
}
ans = ans % mod * C(num[u], d[u]);
if(u == 1)
continue;
num[p[u]] += (num[u] - d[u]);
deg[p[u]]--;
if(deg[p[u]] == 0)
q.push(p[u]);
}
cout << ans % mod << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
init();
ll T = 1;
// cin >> T;
while(T--)
solve();
return 0;
}