题目传送门:
luogu
考虑每种颜色有几个。假设已经求出了每种颜色的个数为 d 1 , d 2 ⋯ d D d_1,d_2\cdots d_D d1,d2⋯dD
方案数就是 n ! d 1 ! d 2 ! ⋯ d D ! \frac{n!}{d_1!d_2!\cdots d_D!} d1!d2!⋯dD!n!
考虑这个方案合法的要求。
∑ d i ( m o d    2 ) ≤ n − 2 m \sum d_i (\mod 2) \le n-2m ∑di(mod2)≤n−2m
这是个蛮显然的转化,因为相同颜色两两匹配,那么不能匹配的就是奇数个的哪一个。
考虑求出 g k g_k gk为 ∑ d i ( m o d    2 ) = k \sum d_i (\mod 2)=k ∑di(mod2)=k的方案数,答案就是 ∑ x = 0 n − 2 m g k \sum_{x=0}^{n-2m} g_k ∑x=0n−2mgk
容易想到EGF。
我们考虑随便放 D D D个随机变量的方案数的EGF表示方法:
D n = ⌊ x n ⌋ e D x n ! D^n=\lfloor x_n\rfloor e^{Dx}n! Dn=⌊xn⌋eDxn!
什么意思呢?
e x = ∑ i = 0 ∞ x i i ! e^x=\sum_{i=0}^{\infty}\frac{x^i}{i!} ex=∑i=0∞i!xi,也就是说当前颜色可以放 1 , 2 , ⋯ 1,2,\cdots 1,2,⋯个,并且消除内部顺序的影响,那么放置 D D D个实际上就是卷积 D D D次后的 n n n次幂系数。
如今我们知道有 k k k个要放置的位置是奇数个的。
如何表示只放置奇数个?
∑ i = 0 ∞ x 2 i ( 2 i ) ! = e x − e − x 2 \sum_{i=0}^{\infty}\frac{x^{2i}}{(2i)!}=\frac{e^x-e^{-x}}{2} ∑i=0∞(2i)!x2i=2ex−e−x
偶数个同理为 e x + e − x 2 \frac{e^x+e^{-x}}{2} 2ex+e−x
然后再用 C D k C_D^k CDk选出这若干个位置。
因此可以得到
g k = n ! ⌊ x n ⌋ C D k ( e x − e − x 2 ) k ( e x + e − x 2 ) D − k g_k=n!\lfloor x_n\rfloor C_D^k(\frac{e^x-e^{-x}}{2})^k (\frac{e^x+e^{-x}}{2})^{D-k} gk=n!⌊xn⌋CDk(2ex−e−x)k(2ex+e−x)D−k
⌊ x n ⌋ \lfloor x_n\rfloor ⌊xn⌋这个东西表示取第 n n n项系数
这个东西实际上可以直接推,但是比较麻烦。我们退而求其次,先求至少为 k k k个奇数的答案:
f k = n ! ⌊ x n ⌋ C D k ( e x − e − x 2 ) k e ( D − k ) x f_k=n!\lfloor x_n\rfloor C_D^k(\frac{e^x-e^{-x}}{2})^k e^{(D-k)x} fk=n!⌊xn⌋CDk(2ex−e−x)ke(D−k)x
然后可以得到 f i = ∑ j = i D C j i g j f_i=\sum_{j=i}^DC_j^ig_j fi=∑j=iDCjigj
二项式反演一下可以得到 g i = ∑ j = i D ( − 1 ) j − i C j i f j g_i=\sum_{j=i}^D(-1)^{j-i}C_j^if_j gi=∑j=iD(−1)j−iCjifj
这个东西可以拆组合数用 N T T NTT NTT解决。
然后问题转化为求 f f f
暴力二项式展开: ( e x − e − x ) k = ∑ i = 0 k C k i ( − 1 ) i e − i x e ( k − i ) x (e^x-e^{-x})^k=\sum_{i=0}^kC_k^i(-1)^ie^{-ix}e^{(k-i)x} (ex−e−x)k=∑i=0kCki(−1)ie−ixe(k−i)x
然后得到
f k = n ! ⌊ x n ⌋ C D k 2 − k ∑ i = 0 k C k i ( − 1 ) i e ( k − 2 i ) x e ( D − k ) x = n ! ⌊ x n ⌋ C D k 2 − k ∑ i = 0 k C k i ( − 1 ) i e ( D − 2 i ) x f_k=n!\lfloor x_n\rfloor C_D^k2^{-k}\sum_{i=0}^kC_k^i(-1)^ie^{(k-2i)x}e^{(D-k)x}=n!\lfloor x_n\rfloor C_D^k2^{-k}\sum_{i=0}^kC_k^i(-1)^ie^{(D-2i)x} fk=n!⌊xn⌋CDk2−k∑i=0kCki(−1)ie(k−2i)xe(D−k)x=n!⌊xn⌋CDk2−k∑i=0kCki(−1)ie(D−2i)x
注意到
e ( D − 2 i ) x ⌊ x n ⌋ = ( D − 2 i ) n n ! e^{(D-2i)x}\lfloor x_n\rfloor=\frac{(D-2i)^n}{n!} e(D−2i)x⌊xn⌋=n!(D−2i)n
所以 f k = n ! C D k 2 − k ∑ i = 0 k C k i ( − 1 ) i ( D − 2 i ) n n ! = C D k 2 − k k ! ∑ i = 0 k 1 ( k − i ) ! ( − 1 ) i ( D − 2 i ) n i ! f_k=n!C_D^k2^{-k}\sum_{i=0}^kC_k^i(-1)^i\frac{(D-2i)^n}{n!}=C_D^k2^{-k}k!\sum_{i=0}^k\frac{1}{(k-i)!}(-1)^i\frac{(D-2i)^n}{i!} fk=n!CDk2−k∑i=0kCki(−1)in!(D−2i)n=CDk2−kk!∑i=0k(k−i)!1(−1)ii!(D−2i)n
∑ \sum ∑前面的那坨只和 k k k有关系不用管,后面是多项式 ( − 1 ) i ( D − 2 i ) n i ! x i (-1)^i\frac{(D-2i)^n}{i!}x^i (−1)ii!(D−2i)nxi和 1 i ! x i \frac{1}{i!}x^i i!1xi卷积结果的第 k k k项。
N T T NTT NTT即可
难点在于:1.问题的转化。2.指数型生成函数的转化。3.二项式反演的简化。
其实我觉得第3个是最难的,因为很容易直接走上直接肝式子的不归路(虽然有人肝出来了)。前面两个比较套路吧,主要还是得有模型积累。
#include
const int N = 262144, P = 998244353;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int A[N], B[N], R[N], f[N], fac[N], ivf[N], w[N], L, D, n, m, InvL;
int Pow(int x, int k) {
int r = 1;
for(;k; k >>= 1, x = 1LL * x * x % P)
if(k & 1)
r = 1LL * r * x % P;
return r;
}
int C(int m, int n) {return 1LL * fac[m] * ivf[n] % P * ivf[m - n] % P;}
void Pre(int m) {
int x = 0; L = 1;
for(;(L <<= 1) <= m; ++x) ;
for(int i = 1;i < L; ++i)
R[i] = R[i >> 1] >> 1 | (i & 1) << x;
int wn = Pow(3, (P - 1) / L); w[0] = 1;
for(int i = 1;i < L; ++i)
w[i] = 1LL * w[i - 1] * wn % P;
InvL = Pow(L, P - 2);
}
void NTT(int *F) {
for(int i = 0;i < L; ++i)
if(R[i] > i)
std::swap(F[i], F[R[i]]);
for(int i = 1, d = L >> 1; i < L; i <<= 1, d >>= 1)
for(int j = 0;j < L; j += i << 1) {
int *l = F + j, *r = F + i + j, *p = w, tp;
for(int k = i; k--; ++l, ++r, p += d)
tp = 1LL * *p * *r % P, *r = (*l - tp) % P, *l = (*l + tp) % P;
}
}
int main() {
D = ri(); n = ri(); m = ri();
if(n < (m << 1)) return puts("0"), 0;
if(D <= n - (m << 1)) return printf("%d\n", Pow(D, n)), 0;
fac[0] = 1;
for(int i = 1;i <= D; ++i)
fac[i] = 1LL * fac[i - 1] * i % P;
ivf[D] = Pow(fac[D], P - 2);
for(int i = D; i; --i)
ivf[i - 1] = 1LL * ivf[i] * i % P;
for(int i = 0, w = 1;i <= D; ++i, w = -w)
A[i] = 1LL * w * Pow(D - (i << 1), n) * ivf[i] % P, B[i] = ivf[i];
Pre(D << 1);
NTT(A); NTT(B);
for(int i = 0;i < L; ++i)
A[i] = 1LL * A[i] * B[i] % P;
NTT(A);
for(int i = 0;i <= D; ++i)
f[i] = 1LL * A[L - i & L - 1] * InvL % P * C(D, i) % P * fac[i] % P * Pow(2, P - 1 - i) % P;
for(int i = 0, w = 1;i <= D; ++i, w = -w)
A[i] = 1LL * f[D - i] * fac[D - i] % P, B[i] = w * ivf[i];
for(int i = D + 1; i < L; ++i)
A[i] = B[i] = 0;
NTT(A); NTT(B);
for(int i = 0;i < L; ++i)
A[i] = 1LL * A[i] * B[i] % P;
NTT(A); long long ans = 0;
for(int i = 0;i <= n - (m << 1); ++i) {
int g = 1LL * A[L - (D - i) & L - 1] * InvL % P * ivf[i] % P;
ans += g;
}
printf("%d\n", (ans % P + P) % P);
return 0;
}