A
题意:
读入一个由<
和>
构成的字符串,在最开始,最后,字符之间可以填上任意数字,任意两个相邻数字之间必须满足字符代表的大小关系。求问最后填入的数字组成的数组最少有多少对逆序对。
题解:
签到。
<
可以不去考虑,因为不会对答案造成影响。
>
如果不是在连续段内,也可以不去考虑,因为最优解必然是这部分贡献为 0 的。只需要考虑连续的 >
个数求解即可。
B
题意:
\(n\) 堆石子,Alice 先手,Bob后手,假设最多只能取走一堆非空石子中 \([1,k]\) 任意个数的石子。
如果不存在 \(k\) 导致 Alice 存在必胜策略,输出 0。
如果不存在最大的 \(k\) 使得 Alice 存在必胜策略,输出 -1。
输出最大的 \(k\)。
题解:
最开始考虑很不全面,WA 了好几次
Nim 游戏扩展,回想 \([1,k]\) 这样的 Nim 游戏的必胜策略是什么,是:令 \(\text{SG}(x) = x \bmod (k+1)\),则 \(f=\text{SG}(a_1) \oplus \cdots \oplus \text{SG}(a_n) = 0\) 是必败情况。
考虑从上到下枚举 \(k\) 的暴力做法,当最初情况,即 \(k+1 \ge \max(a_i)\) 的时候,如果 \(f\) 已经非 0,那么输出 -1。
否则,当某一刻,\(f\) 不是 0 的时候,输出此时的 \(k\),如果到最后还没输出,那么答案就是 \(0\)。
考虑加速这个过程,考虑到答案应该是,到输出之前,无论对于哪个 \(k\),\(f\) 都是 0,考虑 \(\bmod\) 操作只会影响较大的元素,那么考虑排完序后的 \(a_n\),根据模 \(k+1\) 后是否被影响,可以分为前 \(x\) 个元素和 后 \(n - x\) 个元素,如果前 \(x\) 个此时 \(f\) 不为 0,那么 \(k+1=a_{x+1}\) 会导致 \(a_{x+1}\) 变为 0,此时就可能对最后的 \(f\) 造成影响。如此这般循环考虑即可。
C
题意:
有一个长度为 \(N\) 的,仅由 ABC
三种字符组成的字符串。你可以进行 \([0,k]\) 次操作,每次操作是选择字符串中任意两个字符,求得到的字符串由多少种。
题解:
不会写这种东西,卡麻了
考虑假设知道一个目标字符串 \(T\) ,如何求由最开始知道的字符串 \(S\) 求得需要的最少操作次数。
令 \(C[ca][cb]\) 表示有多少个对应位置上 \(S_x = ca\) 而 \(T_x = cb\)。每次操作可以使得 \(C[ca][cb]\) 和 \(C[cb][ca]\) 分别减 1。
经过一定次数的操作后,必然可以得到某一些 \(C[ca][cb]\) 为 0,此时的考虑 \(C\) 数组有何关系。
手玩一下发现,\(C[A][B] = C[B][C] = C[C][A]\),\(C[B][A] = C[C][B] = C[A][C]\)。对于一对没配对的 ABC
可以通过 ABC
-> ACB
-> CAB
这样用 2 次操作,让三个对应的 \(C\) 分别减去 1。
考虑限制这个 \(C\) 数组在特定情况下分别计数。
发现需要考虑的有四个自由度:1. 前一种操作的三对 \((x,y)=(A,B),(B,C),(A,C)\) 操作分别的进行次数,2. 后一种操作的次数。
计数部分是一个较前面内容简单的组合数相乘,具体可以看代码。
(思路来源:ARC official Editorial)
Code:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using LF = double;
using pii = pair<int, int>;
#define fir first
#define sec second
#define mk make_pair
constexpr int N = 250005, M = 105, mod = 998244353;
int n, k;
LL fac[N], inv[N], cnt[5];
string s;
LL ans;
LL Cal(LL m, LL a) {
if (m < a) return 0;
return fac[m] * inv[m - a] % mod * inv[a] % mod;
}
void work() {
cin >> n >> k >> s;
for (auto c : s)
cnt[c - 'A']++;
for (int i = 0; 2 * i <= k; ++i)
for (int a = 0; a + 2 * i <= k; ++a) // ab
for (int b = 0; b + 2 * i + a <= k && i + a + b <= cnt[0]; ++b) // ac
for (int c = 0; a + b + c + 2 * i <= k && i + a + c <= cnt[1] && i + b + c <= cnt[2]; ++c) { // bc
LL mans = 1, mas = 1;
mans = Cal(cnt[0], i + a + b) % mod * Cal(i + a + b, i + a) % mod * Cal(cnt[2], i + b + c) % mod * Cal(i + b + c, i + b) % mod * Cal(cnt[1], i + c + a) % mod * Cal(i + a + c, i + c) % mod;
mas *= Cal(cnt[0], i + a + b) % mod * Cal(i + a + b, i + b) % mod * Cal(cnt[2], i + b + c) % mod * Cal(i + b + c, i + c) % mod * Cal(cnt[1], i + c + a) % mod * Cal(i + a + c, i + a) % mod;
ans = (ans + mas + (i == 0 ? 0 : 1) * mans) % mod;
}
cout << ans << endl;
}
LL fpow(LL x, int b) {
LL res = 1;
while (b) {
if (b & 1) res = res * x % mod;
x = x * x % mod;
b >>= 1;
}
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
fac[0] = 1; n = 250000;
for (int i = 1; i <= n; ++i) {
fac[i] = fac[i - 1] * i % mod;
}
inv[n] = fpow(fac[n], mod - 2);
for (int i = n - 1; ~i; --i) {
inv[i] = inv[i + 1] * (i + 1) % mod;
}
int T = 1;
while (T--) work();
return 0;
}
标签:ARC168,int,题解,ca,cb,考虑,LL
From: https://www.cnblogs.com/rongnian/p/17850828.html