二項式反演小記

學習來源,orz

什麼是反演?

類似這樣fn=i=0nAn,igif_n=\sum_{i=0}^n A_{n,i}* g_i,現在已知ff,要你求gg.這就是一個反演,即一個反推的過程.

假設gn=i=0nBn,ifig_n=\sum_{i=0}^n B_{n,i}*f_i.

這顯然AB=EA*B=E,得到單位矩陣,也就是滿足fn=i=0nAn,ij=0nBi,jfi=i=0n[AB]n,jfi=i=0n[i=n]fif_n=\sum_{i=0}^n A_{n,i}*\sum_{j=0}^n B_{i,j}*f_i=\sum_{i=0}^n [A*B]_{n,j}*f_i=\sum_{i=0}^n [i=n]*f_i.

即求和套反演爲自身.

一些反演:莫比烏斯反演,二項式反演…(其實我就會這倆)

正題:二項式反演

二項式反演有三種形式:(0nm)(0\le n\le m)

  1. fn=i=0n(1)i(ni)gign=i=0n(1)i(ni)fif_n=\sum_{i=0}^n (-1)^i \dbinom n i g_i\Leftrightarrow g_n=\sum_{i=0}^n (-1)^i \dbinom n if_i(極強的對稱性)
  2. fn=i=0n(ni)gign=i=0n(1)ni(ni)fif_n=\sum_{i=0}^n \dbinom n i g_i\Rightarrow g_n=\sum_{i=0}^n (-1)^{n-i} \dbinom n i f_i
  3. fn=i=nm(in)gign=i=nm(1)ni(in)fif_n=\sum_{i=n}^m \dbinom i n g_i\Rightarrow g_n=\sum_{i=n}^m (-1)^{n-i} \dbinom i n f_i

下面證明第一條,其他的證明類似,讀者自證不難.
fn=i=0n(1)i(ni)gi=i=0n(1)i(ni)j=0i(1)j(ij)fjf_n=\sum_{i=0}^n (-1)^i \dbinom n i g_i=\sum_{i=0}^n (-1)^i \dbinom n i \sum_{j=0}^i (-1)^j\dbinom i j f_j
=i=0n(1)ij=0i(1)j(ni)(ij)fj =\sum_{i=0}^n (-1)^i \sum_{j=0}^i (-1)^j\dbinom n i \dbinom i j f_j

由於:
(ni)(ij)=n!i!(ni)!i!j!(ij)!=n!(ni)!1j!(ij)!=n!(nj)!j!(nj)!(ni)!(ij)!=(nj)(njni)\dbinom n i \dbinom i j=\dfrac{n!}{i!(n-i)!}*\dfrac{i!}{j!*(i-j)!}=\dfrac{n!}{(n-i)!}*\dfrac{1}{j!*(i-j)!}=\dfrac{n!}{(n-j)!j!}*\dfrac{(n-j)!}{(n-i)!*(i-j)!}=\dbinom n j \dbinom {n-j}{n-i}

00=1()0^0=1(組合中的規定)

所以:
i=0n(1)ij=0i(1)j(ni)(ij)fj \sum_{i=0}^n (-1)^i \sum_{j=0}^i (-1)^j\dbinom n i \dbinom i j f_j
=i=0n(1)ij=0i(1)j(nj)(njni)fj=\sum_{i=0}^n (-1)^i \sum_{j=0}^i (-1)^j\dbinom n j \dbinom {n-j}{n-i}f_j
=j=0n(1)jfj(nj)i=jn(1)i(njni)=\sum_{j=0}^n (-1)^j f_j*\dbinom n j\sum_{i=j}^n(-1)^i*\dbinom{n-j}{n-i}
=j=0n(1)jfj(nj)i=0nj(1)ni(nji)=\sum_{j=0}^n (-1)^j f_j *\dbinom n j*\sum_{i=0}^{n-j}(-1)^{n-i}*\dbinom{n-j}{i}
=j=0n(1)j+nfj(nj)i=0nj(1)i(nji)=\sum_{j=0}^n (-1)^{j+n} f_j *\dbinom n j*\sum_{i=0}^{n-j}(-1)^{i}*\dbinom{n-j}{i}
=j=0n(1)j+nfj(nj)0nj()=\sum_{j=0}^n (-1)^{j+n} f_j *\dbinom n j*0^{n-j}(二項式定理)
=j=0n[j=n]fj=\sum_{j=0}^n [j=n]f_j
證畢!

應用方法:
當有-1的話用第一條.
用第二三條的時候,即"至多"/"至少"量比"正好"量更好計算時.

應用題可以戳最上面的鏈接看.

例題

1.BZOJ 4487: [Jsoi2015]染色問題

傳送門

三維容斥-----對二項式定理的推廣.

fi,j,kf_{i,j,k}表示ijki行j列正好k種顏色的方案數.
定義gi,j,kg_{i,j,k}表示i,j,k至多染i行,j列,k種顏色的方案數.

則有:
{gn,m,c=(c+1)nmgn,m,c=i=0nj=0mk=0cfi,j,k(ni)(mj)(ck)\begin{cases}g_{n,m,c}=(c+1)^{nm}\\g_{n,m,c}=\sum_{i=0}^n \sum_{j=0}^m \sum_{k=0}^c f_{i,j,k}*\dbinom n i \dbinom m j \dbinom c k\end{cases}
反演:
fn,m,c=i=0nj=0mk=0c(1)ni+mj+ckgi,j,k(ni)(mj)(ck)=i=0nj=0mk=0c(1)ni+mj+ck(k+1)ij(ni)(mj)(ck)f_{n,m,c}=\sum_{i=0}^n \sum_{j=0}^m \sum_{k=0}^c (-1)^{n-i+m-j+c-k}g_{i,j,k}*\dbinom n i \dbinom m j \dbinom c k=\sum_{i=0}^n \sum_{j=0}^m \sum_{k=0}^c (-1)^{n-i+m-j+c-k}(k+1)^{ij}*\dbinom n i \dbinom m j \dbinom c k
可理解爲對每一維依次反演.

顯然暴力需要:O(n3logn)O(n^3 \log n)的複雜度.這樣會T^T.
如果預處理冪的話也是O(n3)O(n^3)的,依然過不了.

觀察式子可以發現有冪次和組合數,我們嘗試用二項式定理進行合併.
(1)mj[(k+1)i]jCmj=[(k+1)i1]j(-1)^{m-j}*[(k+1)^i]^j*C_m^j=[(k+1)^i-1]^j

預處理冪->O(n2)O(n^2).
計算複雜度->O(n2logn)O(n^2 \log n).

#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()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#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;

//char buf[size],*p1=buf,*p2=buf;
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;
}/*
二項式反演的三維形式.
g[i][j][k]表示至多i行,j列,k鍾顏色的方案數.(k+1)^(i*j)

總複雜度O(n^2 log(n))
*/ 


2.CF1228E Another Filling the Grid

題意:給你個nnn*n的矩陣,每個格子可填[1,k][1,k],求每行每列的最小值爲1的方案數.

可以發現[2,k][2,k]的每個數是無區別的.

定義狀態f[i][j]ij,f[i][j]=(k1)n(i+j)ijkn2(i+j)n+ijf[i][j]表示至少i行j列不合法的情況數,f[i][j]=(k-1)^{n(i+j)-i*j}k^{n^2-(i+j)n+i*j},g[i][j]ijg[i][j]表示恰好i行j列不合法的情況數

f[a][b]=i=anj=1nCniCnjg[i][j]g[a][b]=i=anj=b(1)2nijCiaCjbf[i][j]f[a][b]=\sum_{i=a}^n \sum_{j=1}^n C_n^i C_n^j g[i][j]\Leftrightarrow g[a][b]=\sum_{i=a}^n\sum_{j=b}(-1)^{2n-i-j}C_i^a C_j^b f[i][j]

g[0][0]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()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#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;

//char buf[size],*p1=buf,*p2=buf;
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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章