Atcoder Grand Contest 058 D - Yet Another ABC String(组合数学+GF)
做了 1.5h 终于搞出来了(
有人 20min 就切了,很恐怖(我不说是谁)……
首先考虑容斥。我们考虑钦定一个位置集合 \(S\) 满足 \(\forall x\in S\),\(s_xs_{x+1}s_{x+2}\) 必须是题目禁止的三个串之一。算出方案数后乘以 \((-1)^{|S|}\) 求和即是答案。
考虑如何计算这个方案数。先讲一个错误的做法,考虑所有 \([x,x+2]\) 区间的并:显然还是若干个区间的并。我们考虑这些区间的长度 \(l_i\),如果 \(l_i\equiv 0\pmod{3}\) 那好办,等价于将 \(a,b,c\) 均减去 \(\dfrac{l_i}{3}\),如果 \(l_i\equiv 1\pmod{3}\) 那情况也是类似的,也可以视作将 \(a,b,c\) 均减去 \(\dfrac{l_i}{3}\),剩余的那一个字母可以视作随便填,但是如果 \(l_i\equiv 2\pmod{3}\) 就不好办了。如果硬要做的话,需要归约到这个问题,有三个数 \(A,B,C\),你需要进行 \(D\) 次形如“选择一个数 \(+1\)”的操作,再进行 \(A+B+C+D\) 次形如“选择一个数 \(-1\)”的操作,要使得最后 \(A=B=C=0\)。这个问题没有简洁的式子可以推,所以这个做法是没有前途的。
考虑到一件事,就是其实我们不用考虑长度为 \(5\) 的区间,因为要组成长度为 \(5\) 的区间,\([1,3],[3,5]\) 是必须要有的,而 \([2,4]\) 有没有不影响方案数,但是容斥系数一个 \(+1\) 一个 \(-1\),抵消掉了,这给我们一个启发,就是能不能只对长度等于某些值的区间进行容斥,从而达到同样的效果呢?答案是肯定的,事实上我们只用对长度 \(\bmod 3=0/1\) 的区间容斥,具体体现是递推式 \(f_0=1,f_i=\sum\limits_{j\equiv 0\pmod{3}}f_{i-j}-\sum\limits_{j\equiv 1\pmod{3}}f_{i-j}\) 只有 \(f_0=f_1=f_2=1\),其余位置都是 \(0\),对应到原题上就是考虑序列 \(a_i=(s_i-i)\bmod 3\),如果 \(a\) 中长度 \(\ge 3\) 的连续段,那么贡献就是 \(0\),否则贡献为 \(1\),有了这个式子之后我们就可以写出以下 DP:
const int MAXN = 300;
const int MOD = 998244353;
void add(int &x, int v) {((x += v) >= MOD) && (x -= MOD);}
void sub(int &x, int v) {((x -= v) < 0) && (x += MOD);}
int fac[MAXN + 5], ifac[MAXN + 5];
void init_fac(int n) {
for (int i = (fac[0] = ifac[0] = ifac[1] = 1) + 1; i <= n; i++)
ifac[i] = 1ll * ifac[MOD % i] * (MOD - MOD / i) % MOD;
for (int i = 1; i <= n; i++) {
fac[i] = 1ll * fac[i - 1] * i % MOD;
ifac[i] = 1ll * ifac[i - 1] * ifac[i] % MOD;
}
}
int a, b, c, n, dp[MAXN + 5][MAXN + 5], res;
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
cin >> a >> b >> c; dp[0][0] = 1; n = a + b + c; init_fac(MAXN);
for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) if (dp[i][j]) {
for (int k = i + 3; k <= n; k += 3) sub(dp[k][j + (k - i) / 3], 3ll * dp[i][j] % MOD);
for (int k = i + 4; k <= n; k += 3) add(dp[k][j + (k - i) / 3], dp[i][j]);
add(dp[i + 1][j], dp[i][j]);
}
for (int i = 0; i <= min(a, min(b, c)); i++) res = (res + 1ll * dp[n][i] * fac[a - i + b - i + c - i] % MOD
* ifac[a - i] % MOD * ifac[b - i] % MOD * ifac[c - i] % MOD) % MOD;
printf("%d\n", res);
return 0;
}
发现可以过样例。于是继续。考虑推 GF,考虑 \(F(x,y)=x+\sum\limits_{i\ge 1}x^{3i+1}y^i-3x^{3i}y^i\),化简一下可以得到 \(F(x,y)=(x-3)·\dfrac{1}{1-x^{3}y}+3=\dfrac{x-3+3(1-x^3y)}{1-x^3y}=\dfrac{x-3x^3y}{1-x^3y}\),而上面的代码中 \(dp_{x,y}\) 对应的二元 GF 则等于 \(\sum\limits_{n\ge 0}F(x,y)^n=\dfrac{1}{1-F(x,y)}=\dfrac{1}{1-\dfrac{x-3x^3y}{1-x^3y}}=\dfrac{1}{\dfrac{1-x+2x^3y}{1-x^3y}}=\dfrac{1-x^3y}{1-x+2x^3y}\)。而最终我们要求所有 \([x^ny^i]G(x,y)\),我们记 \(H(x,y)=\dfrac{1}{1-x+2x^3y}\),发现 \(H(x,y)=\sum\limits_{n\ge 0}(x-2x^3y)^n\)。这样一来 \([x^ny^i]G(x,y)=[x^ny^i]H(x,y)-[x^{n-3}y]H(x,y)\),于是我们只用求 \([x^ny^m]H(x,y)\),发现我们可以直接推出 \((x-2x^3y)^k\) 中选了多少个 \(x\) 和 \(-2x^3y\),分别为 \(n-3m\) 和 \(m\),于是 \([x^ny^m]H(x,y)=\dbinom{n-2m}{m}·(-2)^m\),于是我们做完了。
可能做烦了。更好的做法可以看官方题解。
#include <bits/stdc++.h>
// #include <ext/pb_ds/assoc_container.hpp>
// #include <ext/pb_ds/hash_policy.hpp>
// #include <ext/pb_ds/priority_queue.hpp>
using namespace std;
// using namespace __gnu_pbds;
#define fi first
#define se second
#define fill0(a) memset(a, 0, sizeof(a))
#define fill1(a) memset(a, -1, sizeof(a))
#define fillbig(a) memset(a, 63, sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
#define mt make_tuple
#ifdef LOCAL
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
#else
#define eprintf(...) 1064
#endif
template<typename T1, typename T2> void chkmin(T1 &x, T2 y) {if (x > y) x = y;}
template<typename T1, typename T2> void chkmax(T1 &x, T2 y) {if (x < y) x = y;}
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long double ld;
#ifdef FASTIO
#define FILE_SIZE 1 << 23
char rbuf[FILE_SIZE], *p1 = rbuf, *p2 = rbuf, wbuf[FILE_SIZE], *p3 = wbuf;
#ifdef LOCAL
inline char getc() {return getchar();}
inline void putc(char c) {putchar(c);}
#else
inline char getc() {return p1 == p2 && (p2 = (p1 = rbuf) + fread(rbuf, 1, FILE_SIZE, stdin), p1 == p2) ? -1 : *p1++;}
inline void putc(char x) {*p3++ = x;}
#endif
template<typename T> void read(T &x) {
x = 0; char c = getc(); T neg = 0;
while (!isdigit(c)) neg |= (c == '-'), c = getc();
while (isdigit(c)) x = x * 10 + (c - '0'), c = getc();
if (neg) x = -x;
}
template<typename T> void recursive_print(T x) {
if (!x) return;
recursive_print(x / 10); putc(x % 10 ^ 48);
}
template<typename T> void print(T x) {
if (!x) putc('0'); if (x < 0) putc('-'), x = -x;
recursive_print(x);
}
template<typename T> void print(T x, char c) {print(x); putc(c);}
void readstr(char *s) {
char c = getc();
while (c <= 32 || c >= 127) c = getc();
while (c > 32 && c < 127) s[0] = c, s++, c = getc();
(*s) = 0;
}
void printstr(string s) {for (int i = 0; i < s.size(); i++) putc(s[i]);}
void printstr(char *s) {
int len = strlen(s);
for (int i = 0; i < len; i++) putc(s[i]);
}
void print_final() {fwrite(wbuf, 1, p3 - wbuf, stdout);}
#endif
/*
const int MAXN = 1000;
const int MOD = 998244353;
void add(int &x, int v) {((x += v) >= MOD) && (x -= MOD);}
void sub(int &x, int v) {((x -= v) < 0) && (x += MOD);}
int fac[MAXN + 5], ifac[MAXN + 5];
void init_fac(int n) {
for (int i = (fac[0] = ifac[0] = ifac[1] = 1) + 1; i <= n; i++)
ifac[i] = 1ll * ifac[MOD % i] * (MOD - MOD / i) % MOD;
for (int i = 1; i <= n; i++) {
fac[i] = 1ll * fac[i - 1] * i % MOD;
ifac[i] = 1ll * ifac[i - 1] * ifac[i] % MOD;
}
}
int a, b, c, n, dp[MAXN + 5][MAXN + 5], res;
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
cin >> a >> b >> c; dp[0][0] = 1; n = a + b + c; init_fac(MAXN);
for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) if (dp[i][j]) {
add(dp[i + 1][j], dp[i][j]);
if (i + 3 <= n) sub(dp[i + 3][j + 1], 2ll * dp[i][j] % MOD);
}
for (int i = 0; i <= n; i++) {
printf("%d ", i); int v = dp[n][i];
if (n >= 3 && i) v = (v - dp[n - 3][i - 1] + MOD) % MOD;
if (dp[n][i] > 8e8) printf("%d\n", v - MOD);
else printf("%d\n", v);
res = (res + 1ll * v * fac[a - i + b - i + c - i] % MOD
* ifac[a - i] % MOD * ifac[b - i] % MOD * ifac[c - i] % MOD) % MOD;
}
printf("%d\n", res);
return 0;
}
*/
const int MAXN = 5e6;
const int MOD = 998244353;
int qpow(int x, int e) {
int ret = 1;
for (; e; e >>= 1, x = 1ll * x * x % MOD)
if (e & 1) ret = 1ll * ret * x % MOD;
return ret;
}
int fac[MAXN + 5], ifac[MAXN + 5];
void init_fac(int n) {
for (int i = (fac[0] = ifac[0] = ifac[1] = 1) + 1; i <= n; i++)
ifac[i] = 1ll * ifac[MOD % i] * (MOD - MOD / i) % MOD;
for (int i = 1; i <= n; i++) {
fac[i] = 1ll * fac[i - 1] * i % MOD;
ifac[i] = 1ll * ifac[i - 1] * ifac[i] % MOD;
}
}
int a, b, c, res, n;
int at(int x, int y) {
int B = y, A = x - y * 3;
if (A < 0) return 0;
return 1ll * fac[A + B] * ifac[A] % MOD * ifac[B] % MOD * qpow(MOD - 2, B) % MOD;
}
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
init_fac(MAXN); cin >> a >> b >> c; n = a + b + c;
for (int i = 0; i <= min(min(a, b), c); i++) {
int v = at(n, i);
if (n >= 3 && i) v = (v - at(n - 3, i - 1) + MOD) % MOD;
res = (res + 1ll * v * fac[a - i + b - i + c - i] % MOD
* ifac[a - i] % MOD * ifac[b - i] % MOD * ifac[c - i] % MOD) % MOD;
}
printf("%d\n", res);
return 0;
}

浙公网安备 33010602011771号