学习来源,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;
}