[HAOI2018]染色
分析
一个神奇的容斥。
题目中的恰好提示我们要把它转化成至少
设
f
[
i
]
f[i]
f[i]表示出现至少
i
i
i种出现次数为
S
S
S的颜色的方案数。
首先钦定
C
m
i
C_m^i
Cmi个颜色。
其次钦定
C
n
i
S
C_n^{iS}
CniS个位置。
把他们全排列(可重集的排列)
i
S
!
(
S
!
)
i
\frac{iS!}{(S!)^i}
(S!)iiS!
剩下
m
−
i
m-i
m−i种颜色,
n
−
i
S
n-iS
n−iS个位置,可以瞎放
(
m
−
i
)
n
−
i
S
(m-i)^{n-iS}
(m−i)n−iS
于是
f
[
i
]
=
C
m
i
C
n
i
S
i
S
!
(
S
!
)
i
(
m
−
i
)
n
−
i
S
f[i]=C_m^iC_n^{iS}\frac{iS!}{(S!)^i}(m-i)^{n-iS}
f[i]=CmiCniS(S!)iiS!(m−i)n−iS
注意我们只需要处理出
l
i
m
=
m
i
n
(
n
S
,
m
)
lim=min(\frac{n}{S},m)
lim=min(Sn,m)的
f
f
f
求出这个玩意儿可以通过线性处理阶乘和阶乘逆元做到
O
(
l
i
m
l
o
g
)
O(limlog)
O(limlog)搞出来
剩下根据容斥原理。
A
n
s
[
k
]
=
∑
i
=
k
(
−
1
)
i
−
k
C
i
k
f
[
i
]
Ans[k]=\sum_{i=k}(-1)^{i-k}C_i^kf[i]
Ans[k]=∑i=k(−1)i−kCikf[i]
那个
C
i
k
C_i^k
Cik是因为从每个
f
i
f_i
fi都被
f
k
f_k
fk重复计算了
C
i
k
C_i^k
Cik次。
接下来就是套路了。
A
n
s
[
k
]
=
∑
i
=
k
(
−
1
)
i
−
k
i
!
k
!
(
i
−
k
)
!
f
[
i
]
Ans[k]=\sum_{i=k}(-1)^{i-k}\frac{i!}{k!(i-k)!}f[i]
Ans[k]=∑i=k(−1)i−kk!(i−k)!i!f[i]
A
n
s
[
k
]
⋅
k
!
=
∑
i
=
k
(
−
1
)
i
−
k
(
i
−
k
)
!
f
[
i
]
i
!
Ans[k]\cdot k!=\sum_{i=k}\frac{(-1)^{i-k}}{(i-k)!}f[i]i!
Ans[k]⋅k!=∑i=k(i−k)!(−1)i−kf[i]i!
N
T
T
NTT
NTT即可。
代码
#include<bits/stdc++.h>
const int N = 262144, M = 1e7 + 10, P = 1004535809;
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 w[N], R[N], A[N], B[N], sz[N], f[M], g[M], v[N], tp, L, InvL, n;
int fix(int x) {return (x >> 31 & P) + x;}
int add(int a, int b) {return a += b, a >= P ? a - P : a;}
int mul(int a, int b) {return 1LL * a * b % P;}
int Pow(int x, int k) {
int r = 1;
for(;k; x = mul(x, x), k >>= 1)
if(k & 1)
r = mul(r, x);
return r;
}
int Inv(int x) {return Pow(x, P - 2);}
void Pre(int m) {
L = 1; int x = 0;
for(;(L <<= 1) < m; ++x) ;
for(int i = 1;i < L; ++i)
R[i] = R[i >> 1] >> 1 | (i & 1) << x;
w[0] = 1; int wn = Pow(3, (P - 1) / L);
for(int i = 1;i < L; ++i)
w[i] = mul(w[i - 1], wn);
InvL = Inv(L);
}
void DFT(int *F) {
for(int i = 0;i < L; ++i)
if(i < R[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 + j + i, *p = w, tp;
for(int k = i; k--; ++l, ++r, p += d)
tp = mul(*r, *p), *r = fix(*l - tp), *l = add(*l, tp);
}
}
int C(int m, int n) {return mul(mul(f[m], g[n]), g[m - n]);}
void Get(int n) {
f[0] = 1; for(int i = 1;i <= n; ++i) f[i] = mul(f[i - 1], i);
g[n] = Inv(f[n]); for(int i = n;i; --i) g[i - 1] = mul(g[i], i);
}
int main() {
int n = ri(), m = ri(), s = ri(), lim = std::min(n / s, m);
for(int i = 0;i <= m; ++i) v[i] = ri();
Get(std::max(n, m));
for(int i = 0;i <= lim; ++i) {
int cnt = i * s;
A[i] = mul(mul(C(m, i), C(n, cnt)), mul(f[cnt], Pow(g[s], i)));
A[i] = mul(A[i], mul(Pow(m - i, n - cnt), f[i]));
}
for(int i = 0;i <= lim; ++i)
B[lim - i] = i & 1 ? fix(-g[i]) : g[i];
Pre((lim << 1) + 1);
DFT(A); DFT(B);
for(int i = 0;i < L; ++i)
A[i] = mul(A[i], B[i]);
DFT(A);
int ans = 0;
for(int i = 0;i <= lim; ++i) {
ans = add(ans, mul(mul(v[i], g[i]), mul(A[L - i - lim & L - 1], InvL)));
}
printf("%d\n", ans);
return 0;
}