Solution -「AGC 058D」Yet Another ABC String
\(\mathscr{Description}\)
Link.
给定 \(a,b,c\) 求由 \(a\) 个 \(\chr A\), \(b\) 个 \(\chr B\), \(c\) 个 \(\chr C\) 组成的, 不存在子串 \(\chr{ABC},\chr{BCA},\chr{CAB}\) 的字符串数量. 答案模 \(998244353\).
\(a,b,c\le10^6\).
\(\mathscr{Solution}\)
不合法单元 \(\chr{ABC},\chr{BCA},\chr{CAB}\) 之间可能重叠, 所以我们难以使用平凡的容斥消除合法限制. 引入更广义的容斥? 考虑计算长度为 \(k\) 的一种不合法单元循环串 \(L_k=(\chr{ABC})^{+\infty}[:k]\) 的容斥系数 \(c_k\). 本质上, 这一字符串在容斥过程中由连续的钦定不合法单元拼接而成. 因此当 \(L_k\to L_{k+1}\) 时, 为了使 \(L_{k+1,k+1}\) 被钦定入 \(L_{k+1}\), 必须钦定 \(L_{k+1}[k-1:]\) 不合法. 也就是说, \(k-1\) 必然被钦定为非法单元左端点. 而再前面一个左端点就能取 \(k-2\) 或 \(k-3\), 对应 \(L_k\) 或 \(L_{k-1}\) 的情景. 因此有 \(c_{k+1}=-(c_k+c_{k-1})\), 边界为 \(c_1=1,c_2=0\). 当然, 这也就说明 \(c_k=[k\equiv1\pmod3]-[k\equiv0\pmod3]\).
接下来容斥 DP. 令 \(f(i,j)\) 表示完成了对 \([1,i]\) 的容斥, 有 \(j\) 个字符未被非法单元确定的方案数. 则
注意两个带容斥系数项的处理. 非法单元循环实际上有 \((\chr{ABC})^{\infty},(\chr{BCA})^{+\infty},(\chr{CAB})^{+\infty}\), 当 \(k\bmod3=1\) 时, 需要确定第一个, 但 \(\chr A,\chr B,\chr C\) 的消耗量不一样, 所以需要计入 \(j\); 当 \(k\bmod3=0\) 时, 直接算上三种方案就行.
先来表示出答案,
组合数统一确定了 \(f\) 中的未被确定字符. 可见, 我们需要求 \(f(n,\star)\).
需要优化, 不过后面的过程都很初等了. 引入 \(F(x,y)=\sum_{i,j}f(i,j)x^iy^j\), 那么
令转移系数整体为 \(T(x,y)\), 化简得
代回解得
提取单项系数容易做到 \(\mathcal O(1)\). 那么本题就 \(\mathcal O(n)\) 完成了.
\(\mathscr{Code}\)
/*+Rainybunny+*/
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)
const int MAXN = 3e6, MOD = 998244353;
int n, a, b, c, fac[MAXN + 5], ifac[MAXN + 5], pwr[MAXN + 5];
inline int mul(const int u, const int v) { return 1ll * u * v % MOD; }
inline void subeq(int& u, const int v) { (u -= v) < 0 && (u += MOD); }
inline int sub(int u, const int v) { return (u -= v) < 0 ? u + MOD : u; }
inline void addeq(int& u, const int v) { (u += v) >= MOD && (u -= MOD); }
inline int add(int u, const int v) { return (u += v) < MOD ? u : u - MOD; }
inline int mpow(int u, int v) {
int ret = 1;
for (; v; u = mul(u, u), v >>= 1) ret = mul(ret, v & 1 ? u : 1);
return ret;
}
inline void init() {
fac[0] = pwr[0] = 1;
rep (i, 1, n) fac[i] = mul(i, fac[i - 1]), pwr[i] = mul(2, pwr[i - 1]);
ifac[n] = mpow(fac[n], MOD - 2);
per (i, n - 1, 0) ifac[i] = mul(i + 1, ifac[i + 1]);
}
inline int bino(const int u, const int v) {
return u < v ? 0 : mul(fac[u], mul(ifac[v], ifac[u - v]));
}
/** Calculate [x^uy^v](1-xy+2x^3)^{-1}. */
inline int calc(const int u, const int v) {
if ((u + 2 * v) % 3) return 0;
int k = (u + 2 * v) / 3;
if (k < v) return 0;
return mul(bino(k, v), (k - v & 1 ? sub : add)(0, pwr[k - v]));
}
int main() {
scanf("%d %d %d", &a, &b, &c);
n = a + b + c, init();
int ans = 0;
rep (i, 0, std::min({ a, b, c })) {
addeq(ans, mul(mul(fac[n - 3 * i], mul(ifac[a - i],
mul(ifac[b - i], ifac[c - i]))),
sub(calc(n, n - 3 * i), calc(n - 3, n - 3 * i))));
}
printf("%d\n", ans);
return 0;
}