1. 程式人生 > 實用技巧 >二項式反演 ~ Inversion of the Binomial

二項式反演 ~ Inversion of the Binomial

二項式反演 ~ Inversion of the Binomial

前置知識 容斥原理

眾所周知

\[\big | \bigcup_{i=1}^n A_i \big |=\sum_i \big|A_i \big|- \sum_{i<j}\big|A_i \cap A_j\big|+\sum_{i<j<k}\big|A_i\cap A_j \cap A_k\big|- \cdots+(-1)^{n-1} \sum_{a_i<a_{i+1}} \big|\bigcap_{i-1}^n A_{a_i}\big| \]

二項式反演

\(\complement A\)\(A\)

的補集。設全集為 \(S\) ,則有:

\[A \cup B=S-\complement A \cap \complement B \]

我們可以對容斥的式子進行變形:

\[\big|\bigcap_{i=1}^n \complement A_i\big|=\big|S\big|-\sum_i \big|A_i \big|+ \sum_{i<j}\big|A_i \cap A_j\big|-\sum_{i<j<k}\big|A_i\cap A_j \cap A_k\big|+ \cdots+(-1)^n \sum_{a_i<a_{i+1}} \big|\bigcap_{i-1}^n A_{a_i}\big| \tag{1} \]

將容斥的式子中 \(A_i\) 替換為 \(\complement A_i\) ,再進行相同的變形:

\[\big|\bigcap_{i=1}^n A_i\big|=\big|S\big|-\sum_i \big|\complement A_i \big|+ \sum_{i<j}\big|\complement A_i \cap \complement A_j\big|-\sum_{i<j<k}\big|\complement A_i\cap \complement A_j \cap \complement A_k\big|+ \cdots+(-1)^n \sum_{a_i<a_{i+1}} \big|\bigcap_{i-1}^n \complement A_{a_i}\big| \tag{2} \]

考慮一種特殊情況:對於任意一個集族 \(\mathcal{U}=\{A_1,A_2,A_3,\cdots,A_n\}\) ,其中任意 \(i\) 個集合的交集都為 \(g(i)\) ,則:

\[g(n)=\big|\bigcap_{a_i<a_{i+1}}^n A_{a_i}\big| \]

並且定義 \(g(0)=|S|\)

類似地,令

\[f(n)=\big|\bigcap_{a_i<a_{i+1}}^n \complement A_{a_i}\big| \]

\(f,g\) 代入 \((1),(2)\) 兩式中得:

\[f(n)=\sum_{i=0}^n (-1)^i {n \choose i}g(i) \iff g(n)=\sum_{i=0}^n (-1)^i{n \choose i}f(i) \]

二項式反演的不同形式

形式一

\[f(n)=\sum_{i=0}^n (-1)^i {n \choose i}g(i) \iff g(n)=\sum_{i=0}^n (-1)^i{n \choose i}f(i) \]

就是上面那個式子。

形式二

\[f(n)=\sum_{i=0}^n {n \choose i} g(i) \iff g(n)=\sum_{i=0}^n(-1)^{n-i}{n \choose i}f(i) \]

這個形式比較好推。不難發現其中 \(g(i)\) 即為形式一中的 \((-1)^ig(i)\) ,代入即可。

形式三

\[f(n)=\sum_{i=n}^m {i \choose n}g(i) \iff g(n)=\sum_{i=n}^m(-1)^{i-n} {i \choose n} f(i) \]

將右式代入左式可得證。


BZOJ2839 集合計數

題面

一個有 \(N\) 個元素的集合有 \(2^N\) 個不同的子集。選出若干個子集(至少一個),使得其交集的元素個數恰好為 \(K\),求有多少種取法。

題解

\(f(k)\) 表示選定 \(k\) 個交集元素的方案數,則有:

\[f(k)={n \choose k}(2^{2^{n-k}}-1) \]

式中 \({n \choose k}\) 表示選定 \(k\) 個元素的方案數。選定 \(k\) 個元素後,還剩下 \(n-k\) 個元素,即有 \(2^{n-k}\) 個集合可以選擇,即有 \(2^{2^{n-k}}-1\) 種選擇集合的方案(至少選一個集合,除去空集)。

\(g(i)\) 表示交集元素恰好為 \(i\) 個的方案數。因為選定 \(k\) 個交集元素後,實際的交集元素個數只會大於等於 \(k\) ,則有:

\[f(k)=\sum_{i=k}^n {i \choose k} g(i) \]

這就是一個典型的二項式反演的形式三,可以得到:

\[g(k)=\sum_{i=k}^n (-1)^{i-k} {i \choose k} f(i) =\sum_{i=k}^n (-1)^{i-k} {i \choose k} {n \choose i}(2^{2^{n-i}}-1) \]

於是就可以 \(O(n)\)\(g(k)\) 了。

\(\text{Code:}\)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 1000005
#define R register
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
const lxl mod=1e9+7;

inline lxl read()
{
	lxl x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}

lxl N,K;
lxl inv[maxn],invf[maxn],fac[maxn];

inline void init()// 預處理一下逆元
{
	inv[1]=invf[1]=invf[0]=fac[1]=fac[0]=1;
	for(lxl i=2;i<=N;++i)
	{
		inv[i]=(-(mod/i)*inv[mod%i]%mod+mod)%mod;
		invf[i]=invf[i-1]*inv[i]%mod;
		fac[i]=fac[i-1]*i%mod;
	}
}

inline lxl C(int n,int m)
{
	return fac[n]*invf[n-m]%mod*invf[m]%mod;
}

int main()
{
	// freopen("P2839.in","r",stdin);
	N=read(),K=read();
	init();
	lxl ans=0,tmp=1,type=((N-K)&1)?-1:1;
	for(int i=N;i>=K;--i)
	{
		ans=(ans+type*C(i,K)%mod*C(N,i)%mod*tmp%mod+mod)%mod;
		tmp=tmp*(tmp+2)%mod;
		type=-type;
	}
	printf("%lld\n",ans);
	return 0;
}