[AGC058D] Yet Another ABC String 题解
非常棒的容斥题。
思路
由于我们要求的是不存在子串 ABC
,BCA
和 CAB
的字符串。
我们可以对这个进行容斥。
如何容斥?
假如我们容斥有多少个不合法的子串,这样是非常不好计算的。
但是我们可以观察这道题的一些性质。
其实你从上一个容斥就可以看出,对于一个类似 ABCA
的串,它的长度只有 \(4\),但是却包含了 \(2\) 个不合法的子串。
可以发现我们可以提出来一个极长的不合法子串。
这是由于他给的字符串的特殊性质。
那么就容易想到我们可以容斥它有多少个极长的不合法子串。
这个是比较好容斥的。
在容斥的时候,由于我们不需要关心他的长度,我们只需要限定他一定是一个不合法子串,并且是极长的即可。
那么可以通过钦定起点来计数。
首先假设有 \(i\) 个极长的不合法子串。
那么我们其它的字符可以随意排列:
\[\binom{a-i+b-i+c-i}{a-i,b-i,c-i}
=\frac{(a+b+c-3\times i)!}{(a-i)!(b-i)!(c-i)!}
\]
由于我们钦定的是起点,那么假如钦定的起点是 \(s\),那么 \(s-1,s,s+1\) 必定不是一个不合法子串。
所以 A
后面可以接 ABC
和 CAB
。
B
后面可以接 ABC
和 BCA
。
C
后面可以接 BCA
和 CAB
。
每个点都是两种情况。
所以方案数可以用插板法算:
\[\binom{a+b+c-i-i-1}{a+b+c-i-i-i-1}\times 2^i
\]
当然,这个答案还少了一种情况,我们的不合法串还可以在开头。
所以还有:
\[\binom{a+b+c-i-i-1}{a+b+c-i-i-i}\times 2^{i-1}\times 3
\]
最后不要忘记容斥系数。
预处理组合数就可以了。
时间复杂度:\(O(n)\)。
Code
/*
! 如果没有天赋,那就一直重复
! Created: 2024/06/28 15:24:35
*/
#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
// #define int long long
#define mp(x, y) make_pair(x, y)
#define eb(...) emplace_back(__VA_ARGS__)
#define fro(i, x, y) for (int i = (x); i <= (y); i++)
#define pre(i, x, y) for (int i = (x); i >= (y); i--)
inline void JYFILE19();
using i64 = long long;
using PII = pair<int, int>;
bool ST;
const int N = 3e6 + 10;
const int mod = 998244353;
int n, m, k, ans, all, f[N];
namespace Math {
const int I = N; i64 fac[I], inv[I], pw2[I];
template<typename T> inline void add(T &x, i64 y) { x = (x + y >= mod ? x + y - mod : x + y); }
template<typename T> inline void mul(T &x, i64 y) { x = (x * y >= mod ? x * y % mod : x * y); }
inline i64 Add(i64 x, i64 y) { return (x + y >= mod ? x + y - mod : x + y); }
inline i64 Mul(i64 x, i64 y) { return (x * y >= mod ? x * y % mod : x * y); }
inline i64 power(i64 x, i64 y) {
i64 res = 1; while(y) {
if(y & 1) res = res * x % mod;
x = x * x % mod, y /= 2;
}
return res;
}
inline void init(int n = I - 10) {
fac[0] = pw2[0] = 1;
fro(i, 1, n) fac[i] = Mul(fac[i - 1], i);
fro(i, 1, n) pw2[i] = pw2[i - 1] * 2 % mod;
inv[n] = power(fac[n], mod - 2);
pre(i, n, 1) inv[i - 1] = Mul(inv[i], i);
}
inline i64 C(i64 x, i64 y) {
if (x < 0 || y < 0 || x - y < 0) return 0;
return Mul(fac[x], Mul(inv[y], inv[x - y]));
}
} using namespace Math;
inline i64 C(i64 a, i64 b, i64 c) {
return fac[a + b + c] * inv[a] % mod * inv[b] % mod * inv[c] % mod;
}
signed main() {
JYFILE19();
cin >> n >> m >> k, init(all = n + m + k);
fro(i, 0, min({n, m, k})) {
i64 res = C(n - i, m - i, k - i);
if (i) {
int x = Mul(C(all - i - i - 1, all - i - i - i - 1), pw2[i]);
int y = Mul(C(all - i - i - 1, all - i - i - i), pw2[i - 1]) * 3 % mod;
res = res * (x + y) % mod;
}
add(ans, (i & 1 ? mod - res : res));
}
cout << ans << "\n";
return 0;
}
bool ED;
inline void JYFILE19() {
// freopen("", "r", stdin);
// freopen("", "w", stdout);
srand(random_device{}());
ios::sync_with_stdio(0), cin.tie(0);
double MIB = fabs((&ED - &ST) / 1048576.), LIM = 1024;
cerr << "MEMORY: " << MIB << endl, assert(MIB <= LIM);
}