[计数] [容斥] 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\)

数数好题捏。

需要有以下的观察:

  1. 三种不同的不合法串就是 \(ABC\) 分别以 \(A,B,C\) 开头循环得到的串。
  2. 非法串存在传递性。这里的非法串 \([l,r]\) 指满足 \(i\in [l,r-2]\) 均为不合法串的起点。由前者易知若两个非法串相交,则会得到合并一个更大的非法串。

直接对非法串容斥并不是很好做。然后本题很巧的一步来了:对极大非法串的起点容斥。

好处有两点:

  1. 无需枚举非法串的长度,只需保证其 \(\ge 3\),也就是只填 \(3\) 位即可。
  2. “极大非法串的起点”很好维护,先确定非法串位置而不填,那么每个串恰有 \(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;
}

posted @ 2026-01-12 20:14  Zwi  阅读(1)  评论(0)    收藏  举报