很水,可以选择性阅读。
例题:洛谷 P5135 painting
给定一个网格,每一列一个元素涂成黑色,求单调下降与单调不升的方案数,对 1 0 9 + 7 10^9+7 109+7 取模。
单调下降 :显然 C n m C _n^m Cnm
单调不升 :显然 C n + m – 1 m C_{n + m – 1}^m Cn+m–1m
考虑位置 a i + i a_i + i ai+i 是单调的,且在 [ 2 , n + m ] [ 2 , n + m ] [2,n+m] 区间内,选 m 种,共有 C n + m – 1 m C_{n + m – 1}^m Cn+m–1m 种选择方式。
#include
#include
#define MAXN 1000005
using namespace std;
const int mod=1e9+7;
int inv[MAXN];
int C(long long n,long long m){
if(n<m) return 0;
int ans=1;
for(long long i=1;i<=m;i++)
ans=1ll*(n-i+1)%mod*inv[i]%mod*ans%mod;
return ans;
}
void solve(){
long long n,m,opt;
cin >> n >> m >> opt;
if(opt==1){
cout << C(n,m) << endl;
}
else{
cout << C(n+m-1,m) << endl;
}
}
int main(){
inv[1]=1;
for(int i=2;i<MAXN;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
int T; cin >> T;
while(T--) solve();
return 0;
}
例题:洛谷 P8432 「WHOI-2」ぽかぽかの星
一场有两道大模拟的公开赛的签到题。
求满足以下条件的长度为 n n n 的数列的数量,对 1 0 9 + 7 10^9+7 109+7 取模:
分 k k k 奇偶性讨论,将两个不能同时出现的数放在一组。
k k k 为偶数时,有 k 2 \frac{k}{2} 2k 组,枚举选的组数,再计算对于所有组数的答案。
即:
∑ i = 1 k 2 C k 2 i C n − 1 i − 1 2 i \sum_{i = 1}^{\frac{k}{2}} C_{\frac{k}{2}}^iC_{n - 1}^{i - 1}2^i i=1∑2kC2kiCn−1i−12i
k k k 为奇数时,需要再计算 k + 1 2 \frac{k+1}{2} 2k+1 是否出现过的答案。
即:
∑ i = 1 k 2 C k 2 i C n − 1 i − 1 2 i + ∑ i = 1 k 2 C k 2 i C n − 2 i − 1 2 i \sum_{i = 1}^{\frac{k}{2}} {C_{\frac{k}{2}}^iC_{n - 1}^{i - 1}2^i} +\sum_{i = 1}^{\frac{k}{2}} C_{\frac{k}{2}}^iC_{n - 2}^{i - 1}2^i i=1∑2kC2kiCn−1i−12i+i=1∑2kC2kiCn−2i−12i
#include
#include
#define MAXN 5000005
using namespace std;
const int mod=1e9+7;
int Fp(int x,int tms){
int ret=1;
while(tms){
if(tms&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod; tms>>=1;
}
return ret;
}
int fac[MAXN],invfac[MAXN],base2[MAXN];
int C(int n,int m){
if(n<m) return 0;
return 1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}
void solve(){
int n,k,ans=0;
cin >> n >> k;
if(n==1){
cout << k << endl;
return;
}
if(k%2==1){
for(int i=1;i<=k/2;i++){
ans=(ans+1ll*C(k/2,i)*C(n-1,i-1)%mod*base2[i]%mod)%mod;
ans=(ans+1ll*C(k/2,i)*C(n-2,i-1)%mod*base2[i]%mod)%mod;
}
cout << ans << endl;
}
else{
for(int i=1;i<=k/2;i++){
ans=(ans+1ll*C(k/2,i)*C(n-1,i-1)%mod*base2[i]%mod)%mod;
}
cout << ans << endl;
}
}
int main(){
fac[0]=base2[0]=1;
for(int i=1;i<MAXN;i++) fac[i]=1ll*fac[i-1]*i%mod,base2[i]=(2ll*base2[i-1])%mod;
invfac[MAXN-1]=Fp(fac[MAXN-1],mod-2);
for(int i=MAXN-2;i>=0;i--) invfac[i]=1ll*invfac[i+1]*(i+1)%mod;
int T; cin >> T;
while(T--) solve();
return 0;
}
例题:CF294C Shaass and Lights
有 n n n 盏灯,其中亮了 m m m 盏,现每次可点亮一盏与亮的灯相邻的灯,求将所有的灯都点亮的方案数。
考虑初始时亮的灯把序列分成了 m + 1 m + 1 m+1 段,我们在每次操作时,选择其中的一段点亮一盏灯。根据可重集排列的性质,有:
a n s = ( n − m ) ! ∏ i = 1 m + 1 l e n i ! ans = \frac{(n - m)!}{\prod_{i = 1}^{m + 1} len_i!} ans=∏i=1m+1leni!(n−m)!
其中 l e n i len_i leni 为分离出的第 i i i 个区间的长度。
然后再考虑对于除最左侧的段和最右侧的段以外,其余的段在每次的选择时都有两种方案,选择最左侧的一盏灯点亮,或者选择最右侧的一盏灯点亮,因此,共有 2 n − m − l e n 1 − l e n m + 1 2^{n - m - len_1 - len_{m + 1}} 2n−m−len1−lenm+1 种选择方式。
相乘即得最终答案。
#include
#include
#include
#include
#define MAXN 100005
using namespace std;
const int mod=1e9+7;
int Fp(int x,int tms){
int ret=1;
while(tms){
if(tms&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod; tms>>=1;
}
return ret;
}
int n,m,base2[MAXN],fac[MAXN],invfac[MAXN],a[MAXN];
bool flag[MAXN];
int main(){
ios :: sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> m;
base2[0]=fac[0]=1;
for(int i=1;i<=m;i++) cin >> a[i];
stable_sort(a+1,a+m+1);
for(int i=1;i<=n;i++){
fac[i]=1ll*fac[i-1]*i%mod;
base2[i]=(base2[i-1]+base2[i-1])%mod;
}
invfac[n]=Fp(fac[n],mod-2);
for(int i=n-1;i>=0;i--) invfac[i]=1ll*invfac[i+1]*(i+1)%mod;
int ans=fac[n-m];
for(int i=1;i<=m;i++){
int tmp=a[i]-a[i-1]-1;
if(tmp==0) continue;
ans=1ll*ans*invfac[tmp]%mod;
if(i!=1) ans=1ll*ans*base2[tmp-1]%mod;
}
if(a[m]!=n) ans=1ll*ans*invfac[n-a[m]]%mod;
cout << ans << endl;
return 0;
}
例题:洛谷 P5339 [TJOI2019]唱、跳、rap和篮球
有 A A A 个人喜欢唱, B B B 个人喜欢跳, C C C 个人喜欢 rap , D D D 个人喜欢篮球,选 N N N 个人排成一列,如果连续的 4 4 4 个人分别喜欢唱,跳, rap ,篮球,则他们会讨论蔡徐坤,现需要求出没有人讨论蔡徐坤的排列方案数,有同样爱好的人被视为同样的。
N ≤ 1000 , A , B , C , D ≤ 500 N\leq1000,A,B,C,D\leq500 N≤1000,A,B,C,D≤500 。
定义 f ( n , a , b , c , d ) f(n,a,b,c,d) f(n,a,b,c,d) 为 a a a 个喜欢唱, b b b 个喜欢跳, c c c 个喜欢 rap , d d d 个喜欢篮球的人任意排列成 n n n 个人的方案数,对 998244353 998244353 998244353 取模。
这个东西显然等于
∑ i = 1 a ∑ j = 1 b ∑ k = 1 c ∑ l = 1 d [ i + j + k + l = n ] × n ! i ! j ! k ! l ! \sum_{i = 1}^{a}\sum_{j = 1}^{b}\sum_{k = 1}^{c}\sum_{l = 1}^{d}[i + j + k + l = n] \times \frac{n!}{i!j!k!l!} i=1∑aj=1∑bk=1∑cl=1∑d[i+j+k+l=n]×i!j!k!l!n!
把 n ! n! n! 提出来,里面很显然能 NTT , O ( n log n ) O(n\log n) O(nlogn) 能求。
之后考虑容斥,有:
a n s = ∑ i = 0 ⌊ n 4 ⌋ ( − 1 ) i C n − 3 i i × f ( N − 4 i , A − i , B − i , C − i , D − i ) ans = \sum_{i = 0}^{\lfloor\frac{n}{4}\rfloor}(-1)^i C_{n - 3i}^{i} \times f(N - 4i , A - i , B - i , C - i , D - i) ans=i=0∑⌊4n⌋(−1)iCn−3ii×f(N−4i,A−i,B−i,C−i,D−i)
考虑枚举至少有 i i i 组人讨论蔡徐坤,剩下的人任意排列的方案。我们把连续 4 4 4 个讨论蔡徐坤的人看成一个蔡徐坤,总共 n − 3 i n - 3i n−3i 个人里面挑出 i i i 个人当蔡徐坤即可。
#include
#include
#define MAXN 2005
using namespace std;
const int mod=998244353;
int Fp(int x,int tms){
int ret=1;
while(tms){
if(tms&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod; tms>>=1;
}
return ret;
}
int n,A,B,C,D,a[MAXN<<2],b[MAXN<<2],c[MAXN<<2],d[MAXN<<2],rev[MAXN<<2];
int fac[MAXN],invfac[MAXN];
void NTT(int * a,int lim,int flag){
for(int i=0;i<lim;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int mid=1;mid<lim;mid<<=1){
int tmp=Fp(flag==1?3:Fp(3,mod-2),(mod-1)/(mid<<1));
for(int i=0;i<lim;i+=(mid<<1)){
int x=1;
for(int j=0;j<mid;j++,x=1ll*x*tmp%mod){
int t1=a[i+j],t2=a[i+mid+j];
a[i+j]=(t1+1ll*x*t2%mod)%mod;
a[i+mid+j]=(t1-1ll*x*t2%mod+mod)%mod;
}
}
}
if(flag==-1){
int inv=Fp(lim,mod-2);
for(int i=0;i<lim;i++) a[i]=1ll*a[i]*inv%mod;
}
}
int C_(int n,int m){
if(n<m) return 0;
return 1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}
int main(){
cin >> n >> A >> B >> C >> D;
fac[0]=1;
for(int i=1;i<MAXN;i++) fac[i]=1ll*fac[i-1]*i%mod;
invfac[MAXN-1]=Fp(fac[MAXN-1],mod-2);
for(int i=MAXN-2;i>=0;i--) invfac[i]=1ll*invfac[i+1]*(i+1)%mod;
int ans=0;
for(int i=0;i<=n/4&&i<=A&&i<=B&&i<=C&&i<=D;i++){
int lim=1,l=0;
while(lim<=A+B+C+D-4*i) lim<<=1,l++;
for(int j=0;j<lim;j++){
rev[j]=(rev[j>>1]>>1)|((j&1)<<l-1);
if(j<=A-i) a[j]=invfac[j];
else a[j]=0;
if(j<=B-i) b[j]=invfac[j];
else b[j]=0;
if(j<=C-i) c[j]=invfac[j];
else c[j]=0;
if(j<=D-i) d[j]=invfac[j];
else d[j]=0;
}
NTT(a,lim,1); NTT(b,lim,1); NTT(c,lim,1); NTT(d,lim,1);
for(int i=0;i<lim;i++) a[i]=1ll*a[i]*b[i]%mod*c[i]%mod*d[i]%mod;
NTT(a,lim,-1);
int tmpans=1ll*a[n-4*i]*fac[n-4*i]%mod*C_(n-3*i,i)%mod;
if(i&1) ans=(ans-tmpans+mod)%mod;
else ans=(ans+tmpans)%mod;
}
cout << ans << endl;
return 0;
}
例题:洛谷 P6295 有标号DAG计数
分别求由 1 − n 1 - n 1−n 个点弱联通 DAG 的个数,对 998244353 998244353 998244353 取模, n ≤ 1 0 5 n\leq10^5 n≤105 。
(本来不打算讲的,但既然发的资料里有就讲吧)
首先,考虑不要求弱联通的情况
考虑容斥,设函数 f ( x ) f(x) f(x) 为由 x x x 个点组成的 DAG 的数量,有:
f ( n ) = ∑ i = 1 n ( − 1 ) i − 1 × C n i × f ( n − i ) × 2 i × ( n − i ) f(n) = \sum_{i = 1}^n (-1)^{i - 1}\times C_n^i \times f(n - i) \times 2^{i \times (n - i)} f(n)=i=1∑n(−1)i−1×Cni×f(n−i)×2i×(n−i)
考虑组合意义其实是显然的,枚举至少有多少个出度为 0 0 0 的节点,再容斥一下就做完了,但是因为我们的重点在容斥原理,所以再详细说明一下。
我们考虑,选出 i i i 个出度为 0 0 0 的节点时,剩下的节点仍然构成一个规模为 n − i n - i n−i 的 DAG ,之后再考虑这两部分的关系,我们既然已经钦定了 n − i n - i n−i 个点所形成的 DAG 的形态,那么,连接在这些点之间的边就是确定的,而可能造成不同情况贡献的答案必然在这两部分之间,那么,也就意味着,目前我们拥有选择空间的边总共有 i × ( n − i ) i \times (n - i) i×(n−i) 条,这其中的每一条都有连边与不连边两种方式可供选择。此时,我们保证了构造出的 DAG 至少有 i i i 个出度为 0 0 0 的节点,将 i i i 从 1 1 1 枚举到 n n n ,再容斥一下,就能得出上式。
O ( n log n ) O(n\log n) O(nlogn) 暴力转移显然,不再赘述,我们考虑优化。
考虑 C n i C_n^i Cni 摆出来了,不妨凑个 EGF 形式。
f ( n ) = ∑ i = 1 n ( − 1 ) i − 1 × C n i × f ( n − i ) × 2 i × ( n − i ) f(n) = \sum_{i = 1}^n (-1)^{i - 1}\times C_n^i \times f(n - i) \times 2^{i \times (n - i)} f(n)=i=1∑n(−1)i−1×Cni×f(n−i)×2i×(n−i)
= ∑ i = 1 n ( − 1 ) i − 1 × C n i × f ( n − i ) × 2 C n 2 − C i 2 − C n − i 2 = \sum_{i = 1}^n (-1)^{i - 1}\times C_n^i \times f(n - i) \times 2^{C_n^2 - C_i^2 - C_{n - i}^2} =i=1∑n(−1)i−1×Cni×f(n−i)×2Cn2−Ci2−Cn−i2
= ∑ i = 1 n ( − 1 ) i − 1 × C n i × f ( n − i ) × 2 C n 2 2 C i 2 × 2 C n − i 2 = \sum_{i = 1}^n (-1)^{i - 1}\times C_n^i \times f(n - i) \times \frac{2^{C_n^2}}{2^{C_i^2}\times 2^{C_{n - i}^2}} =i=1∑n(−1)i−1×Cni×f(n−i)×2Ci2×2Cn−i22Cn2
= ∑ i = 1 n ( − 1 ) i − 1 × n ! i ! × ( n − i ) ! × f ( n − i ) × 2 C n 2 2 C i 2 × 2 C n − i 2 = \sum_{i = 1}^n (-1)^{i - 1}\times \frac{n!}{i!\times (n - i)!} \times f(n - i) \times \frac{2^{C_n^2}}{2^{C_i^2}\times 2^{C_{n - i}^2}} =i=1∑n(−1)i−1×i!×(n−i)!n!×f(n−i)×2Ci2×2Cn−i22Cn2
= 2 C n 2 × n ! × ∑ i = 1 n ( − 1 ) i − 1 i ! × 2 C i 2 × f ( n − i ) ( n − i ) ! × 2 C n − i 2 = 2^{C_n^2} \times n! \times \sum_{i = 1}^n \frac{(-1)^{i - 1}}{i!\times 2^{C_i^2}}\times \frac{f(n - i)}{(n - i)! \times 2^{C_{n - i}^2}} =2Cn2×n!×i=1∑ni!×2Ci2(−1)i−1×(n−i)!×2Cn−i2f(n−i)
这样卷积形式就被我们搞出来了,同时还能发现一些优秀的性质,即:
f ( n ) 2 C n 2 × n ! = ∑ i = 1 n ( − 1 ) i − 1 i ! × 2 C i 2 × f ( n − i ) ( n − i ) ! × 2 C n − i 2 \frac{f(n)}{2^{C_n^2}\times n!} = \sum_{i = 1}^n \frac{(-1)^{i - 1}}{i!\times 2^{C_i^2}}\times \frac{f(n - i)}{(n - i)! \times 2^{C_{n - i}^2}} 2Cn2×n!f(n)=i=1∑ni!×2Ci2(−1)i−1×(n−i)!×2Cn−i2f(n−i)
左侧的式子和卷积的右半边的式子是一样的。
定义生成函数:
F ( x ) = ∑ i f ( i ) 2 C i 2 × i ! F(x) = \sum_i \frac{f(i)}{2^{C_i^2}\times i!} F(x)=i∑2Ci2×i!f(i)
G ( x ) = ∑ i ( − 1 ) i − 1 i ! × 2 C i 2 G(x) = \sum_i \frac{(-1)^{i - 1}}{i! \times 2^{C_i^2}} G(x)=i∑i!×2Ci2(−1)i−1
F ( x ) = F ( x ) × G ( x ) + f ( 0 ) = F ( x ) × G ( x ) + 1 F(x) = F(x) \times G(x) + f(0) = F(x) \times G(x) + 1 F(x)=F(x)×G(x)+f(0)=F(x)×G(x)+1
根据初中因式分解知识:
( 1 − G ( x ) ) × F ( x ) = 1 , F ( x ) = 1 1 − G ( x ) (1 - G(x))\times F(x) = 1,F(x) = \frac{1}{1 - G(x)} (1−G(x))×F(x)=1,F(x)=1−G(x)1
F ( x ) F(x) F(x) 可以求,也就意味着现在 f ( x ) f(x) f(x) 也可以求出来。
然后我们再考虑弱连通图与一般图间的关系。
这里有一个结论:定义 A ( x ) A(x) A(x) 为一般图的 EGF , B ( x ) B(x) B(x) 为弱连通图的 EGF ,则 A ( x ) = exp ( B ( x ) ) A(x) = \exp (B(x)) A(x)=exp(B(x)) ,设 f ( x ) f(x) f(x) 的 EGF 为 F ′ ( x ) F'(x) F′(x) , ln ( F ′ ( x ) ) \ln (F'(x)) ln(F′(x)) 就是我们想要的答案。
虽然大家都知道这个结论,但是我们需要证明一下:
首先,定义在一个弱连通图的大小为 i i i 的点集中的答案为 f ( i ) f(i) f(i) ,在一个由 i i i 个大小总和为 j j j 的弱连通点集所组成的点集中的答案为 F i ( j ) F_{i}(j) Fi(j) 。
则:
F n ( m ) = 1 n ∑ i = 0 m C m i × F n − 1 ( m − i ) × f ( i ) F_{n}(m) = \frac{1}{n} \sum_{i = 0}^mC_m^i \times F_{n - 1}(m - i) \times f(i) Fn(m)=n1i=0∑mCmi×Fn−1(m−i)×f(i)
我们设 G n ( x ) G_n(x) Gn(x) 为 F n ( x ) F_n(x) Fn(x) 的 EGF , H ( x ) H(x) H(x) 为 f ( x ) f(x) f(x) 的 EGF 。
很显然这是个用 EGF 卷积的形式,即:
G n = G n − 1 × 1 n × H = H n n ! G_n = G_{n - 1} \times \frac{1}{n} \times H = \frac{H^n}{n!} Gn=Gn−1×n1×H=n!Hn
可以发现这样就变成了 exp ( H ) \exp (H) exp(H) 的泰勒展开形式,证毕。
代码就是一次多项式求逆和多项式 ln \ln ln ,直接把封装好的板子拿来用就行了。
#include
#include
#include
#define MAXN 100005
using namespace std;
const int mod=998244353;
int Fp(int x,int tms){
int ret=1;
while(tms){
if(tms&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod; tms>>=1;
}
return ret;
}
int rev[MAXN<<2];
void NTT(int * a,int lim,int flag){
for(int i=0;i<lim;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int mid=1;mid<lim;mid<<=1){
int tmp=Fp(flag==1?3:Fp(3,mod-2),(mod-1)/(mid<<1));
for(int i=0;i<lim;i+=(mid<<1)){
int x=1;
for(int j=0;j<mid;j++,x=1ll*x*tmp%mod){
int t1=a[i+j],t2=a[i+mid+j];
a[i+j]=(t1+1ll*x*t2%mod)%mod;
a[i+mid+j]=(t1-1ll*x*t2%mod+mod)%mod;
}
}
}
if(flag==-1){
int inv=Fp(lim,mod-2);
for(int i=0;i<lim;i++) a[i]=1ll*a[i]*inv%mod;
}
}
int tmp[MAXN<<2];
void getinv(int deg,int * a,int * b){
if(deg==1) return (void) (b[0]=Fp(a[0],mod-2));
getinv(deg+1>>1,a,b);
int lim=1,l=0;
while(lim<=deg+deg) lim<<=1,l++;
for(int i=0;i<lim;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<l-1);
for(int i=0;i<deg;i++) tmp[i]=a[i];
for(int i=deg;i<lim;i++) tmp[i]=0;
NTT(tmp,lim,1); NTT(b,lim,1);
for(int i=0;i<lim;i++)
b[i]=(2ll*b[i]%mod+mod-1ll*tmp[i]*b[i]%mod*b[i]%mod)%mod;
NTT(b,lim,-1);
for(int i=deg;i<lim;i++) b[i]=0;
}
void Derivation(int * a,int * b,int len){
for(int i=1;i<len;i++) b[i-1]=1ll*i*a[i]%mod;
b[len-1]=0;
}
void Integration(int * a,int * b,int len){
b[0]=0;
for(int i=1;i<len;i++) b[i]=1ll*a[i-1]*Fp(i,mod-2)%mod;
}
int X[MAXN<<2],Y[MAXN<<2];
void get_Ln(int * a,int * b,int len){
Derivation(a,X,len);
getinv(len,a,Y);
int lim=1,l=0;
while(lim<=len+len) lim<<=1,l++;
for(int i=0;i<lim;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<l-1);
NTT(X,lim,1); NTT(Y,lim,1);
for(int i=0;i<lim;i++) X[i]=1ll*X[i]*Y[i]%mod;
NTT(X,lim,-1);
Integration(X,b,len);
}
int a[MAXN<<2],b[MAXN<<2],c[MAXN<<2],fac[MAXN],invfac[MAXN];
int main(){
int n; cin >> n;
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
invfac[n]=Fp(fac[n],mod-2);
for(int i=n-1;i>=0;i--) invfac[i]=1ll*invfac[i+1]*(i+1)%mod;
a[0]=1;
for(int i=1;i<=n;i++){
a[i]=1ll*invfac[i]*Fp(Fp(2,1ll*(1ll*i*(i-1)/2ll)%(mod-1)),mod-2)%mod;
if(i&1) a[i]=mod-a[i];
}
getinv(n+1,a,b);
for(int i=0;i<=n;i++) b[i]=1ll*Fp(2,1ll*(1ll*i*(i-1)/2ll)%(mod-1))*b[i]%mod;
get_Ln(b,c,n+1);
for(int i=1;i<=n;i++) cout << 1ll*c[i]*fac[i]%mod << '\n';
return 0;
}
二项式反演:
形式 1 1 1 :
若:
f ( x ) = ∑ i = 0 x C x i × g ( i ) f(x) = \sum_{i = 0}^xC_x^i\times g(i) f(x)=i=0∑xCxi×g(i)
则:
g ( x ) = ∑ i = 0 x ( − 1 ) x − i × C n i × f ( i ) g(x) = \sum_{i = 0}^x(-1)^{x - i}\times C_n^i\times f(i) g(x)=i=0∑x(−1)x−i×Cni×f(i)
证明:
考虑把 g ( x ) g(x) g(x) 带进去
有:
f ( x ) = ∑ i = 0 x C x i × g ( i ) = ∑ i = 0 x C x i × ∑ j = 0 i ( − 1 ) i − j × C i j × f ( j ) f(x) = \sum_{i = 0}^xC_x^i\times g(i) = \sum_{i = 0}^xC_x^i\times \sum_{j = 0}^i(-1)^{i - j}\times C_i^j \times f(j) f(x)=i=0∑xCxi×g(i)=i=0∑xCxi×j=0∑i(−1)i−j×Cij×f(j)
f ( x ) = ∑ j = 0 x f ( j ) ∑ i = j x C x i × C i j × ( − 1 ) i − j f(x) = \sum_{j = 0}^xf(j)\sum_{i = j}^xC_x^i\times C_i^j\times (-1)^{i - j} f(x)=j=0∑xf(j)i=j∑xCxi×Cij×(−1)i−j
f ( x ) = ∑ j = 0 x f ( j ) ∑ i = j x C x j × C x − j i − j × ( − 1 ) i − j f(x) = \sum_{j = 0}^xf(j)\sum_{i = j}^xC_x^j\times C_{x - j}^{i - j}\times (-1)^{i - j} f(x)=j=0∑xf(j)i=j∑xCxj×Cx−ji−j×(−1)i−j
f ( x ) = ∑ j = 0 x f ( j ) × C x j ∑ t = 0 x − j C x − j t × ( − 1 ) t f(x) = \sum_{j = 0}^xf(j)\times C_x^j \sum_{t = 0}^{x - j}C_{x - j}^{t}\times (-1)^t f(x)=j=0∑xf(j)×Cxjt=0∑x−jCx−jt×(−1)t
当 x ≠ j x \not=j x=j 时,很显然,由二项式定理,后面那堆东西是等于 0 0 0 的,否则等于 1 1 1 。
证毕。
很显然可以发现枚举不一定要从 0 0 0 开始,从任意位置开始枚举都满足这个式子。
形式 2 2 2 :
若:
f ( x ) = ∑ i = x n C i x × g ( i ) f(x) = \sum_{i = x}^n C_i^x\times g(i) f(x)=i=x∑nCix×g(i)
则:
g ( x ) = ∑ i = x n ( − 1 ) i − x × C i x × f ( i ) g(x) = \sum_{i = x}^n (-1)^{i - x}\times C_i^x\times f(i) g(x)=i=x∑n(−1)i−x×Cix×f(i)
将 g ( x ) g(x) g(x) 带入:
f ( x ) = ∑ i = x n C i x × ∑ j = i n ( − 1 ) j − i × C j i × f ( j ) f(x) = \sum_{i = x}^n C_i^x\times \sum_{j = i}^n(-1)^{j - i}\times C_j^i\times f(j) f(x)=i=x∑nCix×j=i∑n(−1)j−i×Cji×f(j)
f ( x ) = ∑ j = x n f ( j ) ∑ i = x j ( − 1 ) j − i × C j i × C i x f(x) = \sum_{j = x}^nf(j)\sum_{i = x}^j(-1)^{j - i}\times C_j^i\times C_i^x f(x)=j=x∑nf(j)i=x∑j(−1)j−i×Cji×Cix
f ( x ) = ∑ j = x n f ( j ) × C j x ∑ i = x j C j − x i − x × ( − 1 ) j − i f(x) = \sum_{j = x}^nf(j)\times C_j^x\sum_{i = x}^jC_{j - x}^{i - x}\times (-1)^{j - i} f(x)=j=x∑nf(j)×Cjxi=x∑jCj−xi−x×(−1)j−i
f ( x ) = ∑ j = x n f ( j ) × C j x ∑ i = x j C j − x j − i × ( − 1 ) j − i f(x) = \sum_{j = x}^nf(j)\times C_j^x\sum_{i = x}^jC_{j - x}^{j - i}\times (-1)^{j - i} f(x)=j=x∑nf(j)×Cjxi=x∑jCj−xj−i×(−1)j−i
f ( x ) = ∑ j = x n f ( j ) × C j x ∑ t = 0 j − x C j − x t × ( − 1 ) t f(x) = \sum_{j = x}^nf(j)\times C_j^x\sum_{t = 0}^{j - x}C_{j - x}^{t}\times (-1)^t f(x)=j=x∑nf(j)×Cjxt=0∑j−xCj−xt×(−1)t
与形式 1 1 1 同理,证毕。
我们考虑这样一件事情,如果 f ( x ) f(x) f(x) 为至少 x x x 个位置满足条件的方案数, g ( x ) g(x) g(x) 为恰好 x x x 个位置满足条件的方案数,这个形式刚好和二项式反演是具有一致性的。
例题:洛谷 P4859 已经没有什么好害怕的了
给定长度为 n n n 的不重序列 a , b a,b a,b ,求将 a , b a,b a,b 任意排列,使得其中 a i > b i a_i > b_i ai>bi 的位置的个数比 b i > a i b_i > a_i bi>ai 的个数多 k k k 个的方案数, n ≤ 2000 n\leq2000 n≤2000 。
令 k = n + k 2 k = \frac{n+k}{2} k=2n+k 。
先将 a , b a,b a,b 排序。
设 d p i , j dp_{i,j} dpi,j 为 a a a 的前 i i i 个元素中,有 j j j 个满足匹配的 b b b 中元素更小的条件的选择方案数,则有 d p i , j = d p i − 1 , j + d p i − 1 , j − 1 × ( n u m i − j + 1 ) dp_{i,j} = dp_{i - 1,j} + dp_{i - 1,j - 1}\times (num_i - j + 1) dpi,j=dpi−1,j+dpi−1,j−1×(numi−j+1) ,其中 n u m i num_i numi 为对于第 i i i 个位置满足匹配的 b b b 中元素个数。(转移很显然,考虑第 i i i 位不匹配或匹配之前未被选过的任意一个元素)。
然后我们设 f ( x ) f(x) f(x) 为至少满足 x x x 个单调关系的方案数,则 f ( x ) = d p n , x × ( n − x ) ! f(x) = dp_{n,x} \times (n - x)! f(x)=dpn,x×(n−x)! 。设 g ( x ) g(x) g(x) 为恰好满足 x x x 个单调关系的方案数。
由二项式反演,可得:
g ( k ) = ∑ i = k n ( − 1 ) i − k × C i k × f ( k ) g(k) = \sum_{i = k}^n(-1)^{i - k}\times C_i^k\times f(k) g(k)=i=k∑n(−1)i−k×Cik×f(k)
直接输出 g ( k ) g(k) g(k) 即可。
#include
#include
#include
#define MAXN 2005
using namespace std;
const int mod=1e9+9;
int Fp(int x,int tms){
int ret=1;
while(tms){
if(tms&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod; tms>>=1;
}
return ret;
}
int a[MAXN],b[MAXN],n,k,dp[MAXN][MAXN],fac[MAXN],invfac[MAXN],f[MAXN];
int C(int n,int m){
if(n<m) return 0;
return 1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}
int main(){
cin >> n >> k;
if(n+k&1) return cout << 0 << endl,0;
k=n+k>>1;
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
invfac[n]=Fp(fac[n],mod-2);
for(int i=n-1;i>=0;i--) invfac[i]=1ll*invfac[i+1]*(i+1)%mod;
for(int i=1;i<=n;i++) cin >> a[i]; sort(a+1,a+n+1);
for(int i=1;i<=n;i++) cin >> b[i]; sort(b+1,b+n+1);
dp[0][0]=1;
for(int i=1;i<=n;i++){
dp[i][0]=1;
for(int j=1;j<=i;j++){
int rb=lower_bound(b+1,b+n+1,a[i])-b-1;
dp[i][j]=dp[i-1][j];
if(rb>=j) (dp[i][j]+=1ll*dp[i-1][j-1]*(rb-j+1)%mod)%=mod;
}
}
for(int i=1;i<=n;i++) f[i]=1ll*dp[n][i]*fac[n-i]%mod;
int ans=0;
for(int i=k;i<=n;i++){
int x=1ll*C(i,k)*f[i]%mod;
if(i-k&1) ans=(ans-x+mod)%mod;
else ans=(ans+x)%mod;
}
cout << ans << endl;
return 0;
}
例题:洛谷 P4491 [HAOI2018] 染色
给定 n , m , s n,m,s n,m,s ,长度为 n n n 的序列, m m m 种颜色染色,求对于所有 k ∈ [ 0 , m ] k\in[ 0 , m ] k∈[0,m] ,恰好 k k k 种颜色出现 s s s 次的方案数。 n ≤ 1 0 7 , m ≤ 1 0 5 n\leq10^7,m\leq10^5 n≤107,m≤105。
设 f ( i ) f(i) f(i) 为至少 i i i 种颜色出现恰好 s s s 次的方案数,则有:
f ( i ) = C m i n ! ( m − i ) n − s i s ! ( n − s i ) ! f(i) = C_m^i\frac{n!(m - i)^{n - si}}{s!(n - si)!} f(i)=Cmis!(n−si)!n!(m−i)n−si
选择 i i i 种颜色,此时将整体染色方式看成一个 i + 1 i + 1 i+1 种颜色的可重集排列,再将除这 i i i 种颜色以外的颜色的方案数乘上即可。
枚举上界不重要,始终枚举到 m m m 即可。
由二项式反演,设恰好 i i i 种颜色出现恰好 s s s 次的方案数为 g ( i ) g(i) g(i) ,则有:
g ( i ) = ∑ j = i m ( − 1 ) j − i × C i j × f ( j ) g(i) = \sum_{j = i}^m(-1)^{j - i}\times C_i^j\times f(j) g(i)=j=i∑m(−1)j−i×Cij×f(j)
= ∑ j = i m ( − 1 ) j − i j ! i ! ( j − i ) ! f ( j ) = \sum_{j = i}^m(-1)^{j - i}\frac{j!}{i!(j - i)!}f(j) =j=i∑m(−1)j−ii!(j−i)!j!f(j)
= 1 i ! ∑ j = i m ( − 1 ) j − i ( j − i ) ! × f ( j ) j ! =\frac{1}{i!}\sum_{j = i}^m\frac{(-1)^{j - i}}{(j - i)!}\times f(j)j! =i!1j=i∑m(j−i)!(−1)j−i×f(j)j!
= 1 i ! ∑ t = 0 m ( − 1 ) t ( t ) ! f ( t + i ) ( t + i ) ! =\frac{1}{i!}\sum_{t = 0}^m\frac{(-1)^{t}}{(t)!}f(t + i)(t + i)! =i!1t=0∑m(t)!(−1)tf(t+i)(t+i)!
设 A ( x ) = ( − 1 ) m − x ( m − x ) ! , B ( x ) = f ( x ) x ! A(x) = \frac{(-1)^{m - x}}{(m - x)!},B(x) = f(x)x! A(x)=(m−x)!(−1)m−x,B(x)=f(x)x! ,第 i i i 项答案为 [ x m + i ] ( A ∗ B ) [x^{m + i}](A * B) [xm+i](A∗B) 。
注意边界条件。
#include
#include
#define MAXN 100005
#define MAXNN 10000005
using namespace std;
const int mod=1004535809;
int n,m,s,w[MAXN];
int a[MAXN<<2],b[MAXN<<2],rev[MAXN<<2],fac[MAXNN],invfac[MAXNN];
int Fp(int x,int tms){
int ret=1;
while(tms){
if(tms&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod; tms>>=1;
}
return ret;
}
int C(int n,int m){
if(n<m) return 0;
return 1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}
void NTT(int * a,int lim,int flag){
for(int i=0;i<lim;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int mid=1;mid<lim;mid<<=1){
int tmp=Fp(flag==1?3:Fp(3,mod-2),(mod-1)/(mid<<1));
for(int i=0;i<lim;i+=(mid<<1)){
int x=1;
for(int j=0;j<mid;j++,x=1ll*x*tmp%mod){
int t1=a[i+j],t2=a[i+mid+j];
a[i+j]=(t1+1ll*x*t2%mod)%mod;
a[i+mid+j]=(t1-1ll*x*t2%mod+mod)%mod;
}
}
}
if(flag==-1){
int inv=Fp(lim,mod-2);
for(int i=0;i<lim;i++) a[i]=1ll*a[i]*inv%mod;
}
}
int main(){
cin >> n >> m >> s;
for(int i=0;i<=m;i++) cin >> w[i];
fac[0]=1;
for(int i=1;i<MAXNN;i++) fac[i]=1ll*fac[i-1]*i%mod;
invfac[MAXNN-1]=Fp(fac[MAXNN-1],mod-2);
for(int i=MAXNN-2;i>=0;i--) invfac[i]=1ll*invfac[i+1]*(i+1)%mod;
for(int i=0;i<=m;i++) a[i]=1ll*Fp(mod-1,m-i)*invfac[m-i]%mod;
for(int i=0;i<=n;i++){
if(i>m||s*i>n) break;
b[i]=1ll*fac[i]*C(m,i)%mod*fac[n]%mod*Fp(invfac[s],i)%mod*Fp(m-i,n-s*i)%mod*invfac[n-s*i]%mod;
}
int lim=1,l=0;
while(lim<=m+m+2) lim<<=1,l++;
for(int i=0;i<lim;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<l-1);
NTT(a,lim,1); NTT(b,lim,1);
for(int i=0;i<lim;i++) a[i]=1ll*a[i]*b[i]%mod;
NTT(a,lim,-1);
int ans=0;
for(int i=0;i<=m;i++) ans=(ans+1ll*w[i]*a[m+i]%mod*invfac[i]%mod)%mod;
cout << ans << endl;
return 0;
}
由 n n n 个点值可以唯一确定一个 n − 1 n - 1 n−1 次多项式。
f ( k ) = ∑ i = 1 n y i ∏ j ≠ i k − x j x i − x j f(k) = \sum_{i = 1}^ny_i\prod_{j \not= i}\frac{k - x_j}{x_i - x_j} f(k)=i=1∑nyij=i∏xi−xjk−xj
考虑模意义下的证明:
对于 n n n 个点值而言,有 f ( k ) ≡ y i ( m o d ( k − x i ) ) f(k)\equiv y_i (\bmod (k - x_i)) f(k)≡yi(mod(k−xi)) ,(将 f ( x ) f(x) f(x) 展开,显然可证),根据 CRT ,有:
f ( k ) = ∑ i = 1 n y i ∏ j = 1 n ( k − x j ) ∏ j ≠ i ( x i − x j ) f(k) = \sum_{i = 1}^n y_i\frac{\prod_{j = 1}^n (k - x_j)}{\prod_{j\not = i}(x_i - x_j)} f(k)=i=1∑nyi∏j=i(xi−xj)∏j=1n(k−xj)
整理可得。
模板题 洛谷 P4781
依照式子计算即可。
#include
#include
#define MAXN 2005
using namespace std;
const int mod=998244353;
int Fp(int x,int tms){
int ret=1;
while(tms){
if(tms&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod; tms>>=1;
}
return ret;
}
int n,k,x[MAXN],y[MAXN];
int main(){
cin >> n >> k;
for(int i=1;i<=n;i++){
cin >> x[i] >> y[i];
}
int ans=0;
for(int i=1;i<=n;i++){
int fz=y[i]%mod,fm=1;
for(int j=1;j<=n;j++){
if(i^j){
fz=1ll*fz*((k-x[j]+mod)%mod)%mod;
fm=1ll*fm*((x[i]-x[j]+mod)%mod)%mod;
}
}
ans=(ans+1ll*fz*Fp(fm,mod-2))%mod;
}
cout << ans << endl;
return 0;
}
例题 CF622F The Sum of the k-th Powers
求 ∑ i = 1 n i k \sum_{i = 1}^ni^k ∑i=1nik , n ≤ 1 0 9 , k ≤ 1 0 6 n\leq10^9,k\leq10^6 n≤109,k≤106 。
我们不加证明地给出一个非常常见的结论, ∑ i = 1 n i k \sum_{i = 1}^ni^k ∑i=1nik 是一个 k + 1 k + 1 k+1 次的多项式,因此,用 k + 2 k + 2 k+2 个点值就可以确定这个多项式,很显然,如果我们取 [ 1 , k + 2 ] [1,k + 2] [1,k+2] ,对于求和号里的式子是可以做到预处理后 O ( 1 ) O(1) O(1) 求的,于是整体的式子就是 O ( n log n ) O(n\log n) O(nlogn) 的,直接输出即可。
#include
#include
#define MAXN 1000005
using namespace std;
int n,k,fac[MAXN],pre[MAXN],suf[MAXN],sum[MAXN];
const int mod=1e9+7;
int Fp(int x,int tms){
int ret=1;
while(tms){
if(tms&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod; tms>>=1;
}
return ret;
}
int main(){
cin >> n >> k;
fac[0]=pre[0]=1;
for(int i=1;i<=k+2;i++){
fac[i]=1ll*fac[i-1]*i%mod;
pre[i]=1ll*(n-i+mod)%mod*pre[i-1]%mod;
sum[i]=(sum[i-1]+Fp(i,k))%mod;
}
suf[k+3]=1;
for(int i=k+2;i>=0;i--)
suf[i]=1ll*(n-i+mod)%mod*suf[i+1]%mod;
int ans=0;
for(int i=1;i<=k+2;i++){
int fz=sum[i],fm=1;
fz=fz*1ll*pre[i-1]%mod*suf[i+1]%mod;
fm=fm*1ll*fac[i-1]%mod*fac[k+2-i]%mod;
if((k+2-i)%2==1) fm=mod-fm;
ans=(ans+1ll*fz*Fp(fm,mod-2)%mod)%mod;
}
cout << ans << endl;
return 0;
}
(证明咕了,能补就补)