學習來源,orz
什麼是反演?
類似這樣fn=∑i=0nAn,i∗gi,現在已知f,要你求g.這就是一個反演,即一個反推的過程.
假設gn=∑i=0nBn,i∗fi.
這顯然A∗B=E,得到單位矩陣,也就是滿足fn=∑i=0nAn,i∗∑j=0nBi,j∗fi=∑i=0n[A∗B]n,j∗fi=∑i=0n[i=n]∗fi.
即求和套反演爲自身.
一些反演:莫比烏斯反演,二項式反演…(其實我就會這倆)
正題:二項式反演
二項式反演有三種形式:(0≤n≤m)
- fn=∑i=0n(−1)i(in)gi⇔gn=∑i=0n(−1)i(in)fi(極強的對稱性)
- fn=∑i=0n(in)gi⇒gn=∑i=0n(−1)n−i(in)fi
- fn=∑i=nm(ni)gi⇒gn=∑i=nm(−1)n−i(ni)fi
下面證明第一條,其他的證明類似,讀者自證不難.
fn=i=0∑n(−1)i(in)gi=i=0∑n(−1)i(in)j=0∑i(−1)j(ji)fj
=i=0∑n(−1)ij=0∑i(−1)j(in)(ji)fj
由於:
(in)(ji)=i!(n−i)!n!∗j!∗(i−j)!i!=(n−i)!n!∗j!∗(i−j)!1=(n−j)!j!n!∗(n−i)!∗(i−j)!(n−j)!=(jn)(n−in−j)
00=1(組合中的規定)
所以:
i=0∑n(−1)ij=0∑i(−1)j(in)(ji)fj
=i=0∑n(−1)ij=0∑i(−1)j(jn)(n−in−j)fj
=j=0∑n(−1)jfj∗(jn)i=j∑n(−1)i∗(n−in−j)
=j=0∑n(−1)jfj∗(jn)∗i=0∑n−j(−1)n−i∗(in−j)
=j=0∑n(−1)j+nfj∗(jn)∗i=0∑n−j(−1)i∗(in−j)
=j=0∑n(−1)j+nfj∗(jn)∗0n−j(二項式定理)
=j=0∑n[j=n]fj
證畢!
應用方法:
當有-1的話用第一條.
用第二三條的時候,即"至多"/"至少"量比"正好"量更好計算時.
應用題可以戳最上面的鏈接看.
例題
1.BZOJ 4487: [Jsoi2015]染色問題
傳送門
三維容斥-----對二項式定理的推廣.
設fi,j,k表示i行j列正好k種顏色的方案數.
定義gi,j,k表示至多染i行,j列,k種顏色的方案數.
則有:
⎩⎨⎧gn,m,c=(c+1)nmgn,m,c=∑i=0n∑j=0m∑k=0cfi,j,k∗(in)(jm)(kc)
反演:
fn,m,c=∑i=0n∑j=0m∑k=0c(−1)n−i+m−j+c−kgi,j,k∗(in)(jm)(kc)=∑i=0n∑j=0m∑k=0c(−1)n−i+m−j+c−k(k+1)ij∗(in)(jm)(kc)
可理解爲對每一維依次反演.
顯然暴力需要:O(n3logn)的複雜度.這樣會T^T.
如果預處理冪的話也是O(n3)的,依然過不了.
觀察式子可以發現有冪次和組合數,我們嘗試用二項式定理進行合併.
即(−1)m−j∗[(k+1)i]j∗Cmj=[(k+1)i−1]j
預處理冪->O(n2).
計算複雜度->O(n2logn).
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()
#define mk make_pair
#define pi pair<int,int>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=410,size=1<<20,mod=1e9+7;
template<class o> void qr(o &x) {
char c=gc; x=0; int f=1;
while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
while(isdigit(c)) x=x*10+c-'0',c=gc;
x*=f;
}
template<class o> void qw(o x) {
if(x/10) qw(x/10);
putchar(x%10+'0');
}
template<class o> void pr1(o x) {
if(x<0)x=-x,putchar('-');
qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
if(x<0)x=-x,putchar('-');
qw(x); puts("");
}
int n,m,c,p[N][N];
ll jc[N],inv[N],ans;
ll C(int x,int y) {return jc[x]*inv[y]%mod*inv[x-y]%mod;}
ll power(ll a,ll b=mod-2) {
ll c=1;
while(b) {
if(b&1) c=c*a%mod;
b=b/2; a=a*a%mod;
}
return c;
}
int main() {
qr(n); qr(m); qr(c);
jc[0]=inv[0]=1; for(int i=1;i<=400;i++) inv[i]=power(jc[i]=jc[i-1]*i%mod);
for(int i=1;i<=c+1;i++)
for(int j=p[i][0]=1;j<=m;j++)
p[i][j]=(ll)p[i][j-1]*i%mod;
for(int j=1,fj=(m&1)?1:-1;j<=m;j++,fj=-fj)
for(int k=0,fk=((c&1)?-1:1)*fj;k<=c;k++,fk=-fk)
(ans += fk*C(m,j)*C(c,k)%mod*power(p[k+1][j]-1,n)%mod) %= mod;
pr2((ans+mod)%mod);
return 0;
}
題意:給你個n∗n的矩陣,每個格子可填[1,k],求每行每列的最小值爲1的方案數.
可以發現[2,k]的每個數是無區別的.
定義狀態f[i][j]表示至少i行j列不合法的情況數,f[i][j]=(k−1)n(i+j)−i∗jkn2−(i+j)n+i∗j,g[i][j]表示恰好i行j列不合法的情況數
f[a][b]=∑i=an∑j=1nCniCnjg[i][j]⇔g[a][b]=∑i=an∑j=b(−1)2n−i−jCiaCjbf[i][j]
g[0][0]即爲答案.
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()
#define mk make_pair
#define pi pair<int,int>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=255,size=1<<20,mod=1e9+7;
template<class o> void qr(o &x) {
char c=gc; x=0; int f=1;
while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
while(isdigit(c)) x=x*10+c-'0',c=gc;
x*=f;
}
template<class o> void qw(o x) {
if(x/10) qw(x/10);
putchar(x%10+'0');
}
template<class o> void pr1(o x) {
if(x<0)x=-x,putchar('-');
qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
if(x<0)x=-x,putchar('-');
qw(x); puts("");
}
int n,m;
ll ans,C[N][N];
ll power(ll a,ll b) {
ll c=1;
while(b) {
if(b&1) c=c*a%mod;
b/=2; a=a*a%mod;
}
return c;
}
int main() {
qr(n); qr(m);
for(int i=0;i<=n;i++) C[i][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
for(int i=0;i<=n;i++)
for(int j=0,k;j<=n;j++) {
k=n*(i+j)-i*j;
ans+=((i+j)&1?-1:1)*C[n][i]*C[n][j]%mod*power(m-1,k)%mod*power(m,n*n-k)%mod;
}
pr2((ans%mod+mod)%mod);
return 0;
}