[计数] [容斥] AT_agc058_d [AGC058D] Yet Another ABC String
posted on 2025-02-24 12:14:39 | under | source
题意:用 \(a,b,c\) 个 \(A,B,C\) 组成字符串,问有多少个串满足不存在 \(ABC,BCA,CAB\) 子串。\(a,b,c\le 10^6\)。
数数好题捏。
需要有以下的观察:
- 三种不同的不合法串就是 \(ABC\) 分别以 \(A,B,C\) 开头循环得到的串。
- 非法串存在传递性。这里的非法串 \([l,r]\) 指满足 \(i\in [l,r-2]\) 均为不合法串的起点。由前者易知若两个非法串相交,则会得到合并一个更大的非法串。
直接对非法串容斥并不是很好做。然后本题很巧的一步来了:对极大非法串的起点容斥。
好处有两点:
- 无需枚举非法串的长度,只需保证其 \(\ge 3\),也就是只填 \(3\) 位即可。
- “极大非法串的起点”很好维护,先确定非法串位置而不填,那么每个串恰有 \(2\) 种填法,只需起点与它前面的字母不同即可。假如前面没有字母就随便填。
现在是容易计数了。具体来说,枚举有 \(i\) 个非法串,先让 \(a+b+c-3i\) 个字母任意排列。然后插入非法串起始部分,先确定位置再确定字母,只需分讨一下是否有非法串在开头部分即可。
\(O(n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ADD(a, b) a = (a + b) % mod
const int N = 4e6 + 5, mod = 998244353, inv2 = 499122177;
int a, b, c, ans, jc[N], jcinv[N];
inline int qstp(int a, int k) {int res = 1; for(; k; a = a * a % mod, k >>= 1) if(k & 1) res = res * a % mod; return res;}
inline int C(int n, int m){
if(n < 0 || m < 0 || n < m) return 0;
return jc[n] * jcinv[m] % mod * jcinv[n - m] % mod;
}
inline int CC(int n, int m){
if(m == 0 && n == 0) return 1;
return C(n + m - 1, m - 1);
}
signed main(){
jc[0] = jcinv[0] = 1;
for(int i = 1; i < N; ++i) jc[i] = jc[i - 1] * i % mod;
jcinv[N - 1] = qstp(jc[N - 1], mod - 2);
for(int i = N - 2; i; --i) jcinv[i] = jcinv[i + 1] * (i + 1) % mod;
cin >> a >> b >> c;
for(int i = 0, pw = 1, pw2 = 1; i <= min(a, min(b, c)); ++i){
int res, res2;
res = jc[a + b + c - 3 * i] * jcinv[a - i] % mod * jcinv[b - i] % mod * jcinv[c - i] % mod;
res2 = CC(i, a + b + c - 3 * i) * pw2 % mod;
ADD(res2, CC(i - 1, a + b + c - 3 * i + 1) * pw2 % mod * inv2 % mod * 3ll % mod);
ADD(ans, res * res2 % mod * pw % mod) % mod;
pw = (pw == 1 ? (mod - 1) : 1);
pw2 = pw2 * 2ll % mod;
}
cout << ans;
return 0;
}

浙公网安备 33010602011771号