[CTS2019]珍珠
题目传送门:
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<bits/stdc++.h>
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;
}