二項式反演與斯特林反演

二項式反演

如果 f[m]=i=mn(ni)g[i]f[m]=\sum_{i=m}^n{n\choose i}g[i]
根據二項式反演:
g[i]=k=in(1)ki(ki)f[k] g[i]=\sum_{k=i}^n(-1)^{k-i}{k\choose i}f[k]
好像是二項式反演的另一種形式?
正常版本:
f[n]=i=0n(ni)g[i]g[n]=i=0n(1)ni(ni)f[i] f[n]=\sum_{i=0}^n{n\choose i}g[i]\\ g[n]=\sum_{i=0}^n(-1)^{n-i}{n\choose i}f[i]
f[n]=i=0n(1)i(ni)g[i]g[n]=i=0n(1)i(ni)f[i] f[n]=\sum_{i=0}^n(-1)^i{n\choose i}g[i]\\ g[n]=\sum_{i=0}^n(-1)^i{n\choose i}f[i]\\

就是在容斥。

斯特林數

  • 第一類斯特林數:把n個標號元素排成m個環的方案數。
    S1(n,m)=S1(n1,m1)+(n1)S1(n1,m)S_1(n,m)=S_1(n-1,m-1)+(n-1)\cdot S_1(n-1,m)有一種組合意義是 nn 個元素的排列,有 mm 個前綴最大值的方案數。可以自然推出:i=0nS1(n,i)=n!\sum_{i=0}^nS_1(n,i)=n!
    根據遞推式還可以寫出 S1(n)S_1(n) 的生成函數:S1(n)=i=0n1(x+i)S_1(n)=\prod_{i=0}^{n-1} (x+i)。我們用分治 FFT 在 nlog2n 的時間計算某一行。

  • 第二類斯特林數:把n個標號元素劃分爲m個相同集合的方案數。
    S2(n,m)=S2(n1,m1)+mS2(n1,m)S_2(n,m)=S_2(n-1,m-1)+m\cdot S_2(n-1,m)
    容斥計算:
    S2(n,m)=1m!i=0m(1)i(mi)(mi)n=i=0m(1)ii !(mi)n(mi)!S_2(n,m)=\frac1{m!}\sum_{i=0}^m(-1)^i{m\choose i}(m-i)^n\\=\sum_{i=0}^m\frac{(-1)^i}{i~!}·\frac{(m-i)^n}{(m-i)!}可以O(n)算某一項,nlogn 計算某一行。

小技巧

nk=i=0nS2(k,i)(ni)i!n^k=\sum_{i=0}^nS_2(k,i){n\choose i}i!
左邊相當於把k個標號球放進n個標號盒子裏。右邊枚舉放在那些盒子裏,由於盒子有標號所以乘上階乘,再乘上斯特林數。

注意到上式的 i 枚舉到 n,實際上可以枚舉到 k,因爲後面的組合數乘階乘可以看做一個 i 次下降冪,如果 k>n 那麼這個下降冪會變成 0,如果 k<n,那麼 i 在 大於 k 的時候前面的斯特林數就會變成 0。也就是:
nk=i=0nS2(k,i)nin^k=\sum_{i=0}^nS_2(k,i)\cdot n^{i\downarrow}
我們把這個式子變得和底數沒什麼關係了。有什麼好處呢?如果我們要維護 aik\sum a_i^k,k 是常數,我們只需要維護 aik\sum a_i^{k\downarrow},最後再 O(k) 計算答案。注意到 xk=xk+kx(k1)x^{k\downarrow}=x^{k\downarrow}+k·x^{(k-1)\downarrow}(考慮組合意義),因此 aik\sum a_i^{k\downarrow} 這個式子是可以在 O(k) 的時間變成 (ai+1)k\sum (a_i+1)^{k\downarrow}

1

給 n, k 和一個長度爲 n 的數字串。要求對每一個 i, 輸出 j=1iF(j,i)∑_{j=1}^i F(j, i)。其中 F(l, r) 爲 l 到 r 內所有數字之和的 k 次方。
n50000,k100n\leq 50000,k\leq 100

直接二項式定理複雜度是 O(nk^2),用上面的式子複雜度是 O(nk*10) 的。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define fir first
#define sec second
#define ld long double
using namespace std;
const int N=100010,mod=1e9+7;
typedef pair <int,int> P;
char s[N];
ll S[110][110],f[110];
int a[N],n,k;
int read()
{
	int x=0;char c=getchar(),flag='+';
	while(!isdigit(c)) flag=c,c=getchar();
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	return flag=='-'?-x:x;
}
void add()
{
	for(int i=k;i>0;i--) f[i]=(f[i]+f[i-1]*i)%mod;
}
int main()
{
	int T=read();
	for(int i=1;i<=100;i++)
	{
		S[i][1]=1;
		for(int j=2;j<=i;j++) S[i][j]=(S[i-1][j-1]+S[i-1][j]*j)%mod;
	}
	while(T--) //f[i]=sigma{F(j,x)的i次下降冪} 
	{
		memset(f,0,sizeof(f));
		n=read(),k=read();
		scanf("%s",s+1);
		for(int i=1;i<=n;i++) a[i]=s[i]-'0';
		for(int i=1;i<=n;i++)
		{
			f[0]=(f[0]+1)%mod;
			for(int j=1;j<=a[i];j++) add();
			int ans=0;
			for(int j=1;j<=k;j++) ans=(ans+S[k][j]*f[j])%mod;
			cout<<ans<<' ';
		}
	}
	return 0;
}
/*by DT_Kang*/

斯特林反演:

f[n]=i=1nS2(n,i)g[i]g[n]=i=1n(1)niS1(n,i)f[i] f[n]=\sum_{i=1}^nS_2(n,i)\cdot g[i]\\ g[n]=\sum_{i=1}^n(-1)^{n-i}\cdot S_1(n,i)\cdot f[i]

相比二項式反演就是把組合數換成斯特林數。

2

定義兩個結點數相同的圖 G1 與圖 G2 的異或爲一個新的圖 G, 其中如果 (u, v) 在 G1 與G2 中的出現次數之和爲 1, 那麼邊 (u, v) 在 G 中, 否則這條邊不在 G 中. 現在給定 s 個結點數相同的圖 G1…Gs, 設 S = {G1, G2, … , Gs}, 請問 S 有多少個子集的異或爲一個連通圖?

10的貝爾數是可以接受的。
用總方案減去不合法的,我們要算的就是g[i]表示有i個聯通塊的方案數。枚舉了點集劃分以後,不同的點集之間的邊是不能存在的,相同點集的邊隨便選,記爲f。但是這樣沒有保證一個點集的點聯通。因此實際上:f[m]=i=mnS2(i,m)g[i]f[m]=\sum_{i=m}^nS_2(i,m)\cdot g[i]
斯特林反演一下:
g[m]=i=mn(1)imS1(i,m)f[i]g[m]=\sum_{i=m}^n(-1)^{i-m}S_1(i,m)\cdot f[i]
只要求g[1],即
g[1]=i=1n(1)i1S1(i,1)f[i]=i=1n(1)i1(i1)!f[i] g[1]=\sum_{i=1}^n(-1)^{i-1}S_1(i,1)\cdot f[i]\\ =\sum_{i=1}^n(-1)^{i-1}(i-1)!\cdot f[i]

那麼問題就變成了如何求f。我們把邊壓成一個longlong,枚舉了集合劃分之後,相當於有一些數,求有多少個子集異或位0。根據線性基的理論,我們求出一個線性基,枚舉線性基外面的任意一個集合,都能在線性基裏找到恰好一個元素,它們的異或爲0。

3

雅禮集訓 方陣

給定一個方陣,每個位置可以填 [1,c] 中的任意一個數,要求填完之後不能有兩行或兩列完全相同,求方案數。n,m5000n,m\leq 5000

這種行列都有限制的題目往往是先考慮一邊,也就是讓行都不等價,這樣列可能存在等價的情況。設 f_n 表示 n 列互補等價的方案數,g_n 表示任意的方案數,那麼實際上 gn=i=1nfnS2(n,i)g_n=\sum_{i=1}^nf_nS_2(n,i),反演一下可以得到 fn=i=1n(1)nignS1(n,i)f_n=\sum_{i=1}^n(-1)^{n-i}g_nS_1(n,i)

容易發現,第二類斯特林數總是和等價類劃分之類的問題相關。

4

計算
i=0nj=0nS(i,j)j!2j \sum_{i=0}^n\sum_{j=0}^nS(i,j)·j!·2^j

解法1:
第二類斯特林數可以展開:
S(n,m)=1m!i=0m(1)im!i!(mi)!(mi)n=i=0m(1)ii !(mi)n(mi)! S(n,m)=\frac 1{m!}\sum_{i=0}^m(-1)^i·\frac{m!}{i!(m-i)!}·(m-i)^n\\ =\sum_{i=0}^m\frac{(-1)^i}{i~!}·\frac{(m-i)^n}{(m-i)!}
是一個卷積。因此我們可以在 O(nlogn)O(n\log n) 的時間裏計算 S(n,0),S(n,1),,S(n,n)S(n,0),S(n,1),\dots,S(n,n)

代回原式:
i=0nj=0nS(i,j)j!2j=i=0nj=0nj!2jk=0j(1)kk !(jk)i(jk)!=j=0nj!2jk=0j(1)kk !i=0n(jk)i(jk)! \sum_{i=0}^n\sum_{j=0}^nS(i,j)·j!·2^j =\sum_{i=0}^n\sum_{j=0}^nj!·2^j\sum_{k=0}^j\frac{(-1)^k}{k~!}·\frac{(j-k)^i}{(j-k)!}\\ =\sum_{j=0}^nj!·2^j\sum_{k=0}^j\frac{(-1)^k}{k~!}·\frac{\sum_{i=0}^n(j-k)^i}{(j-k)!}

解法2:
考慮第二類斯特林數的意義,是把 nn 個不同的球放入 mm 個相同盒子裏的方案數。由於盒子非空,因此乘上一個 j!j! 以後就是放進不同盒子的方案數了。
我們令 gng_n 表示 i=0nS(n,i)i!2i\sum_{i=0}^nS(n,i)·i!·2^i。如果不考慮 2i2^i,也就是把 nn 個不同的球放進任意多個不同的盒子裏的方案數。我們枚舉最後一個盒子放幾個球,可以寫出遞推式:
gn=i=1n2(ni)gnign!n!=i=1n2i!gni(ni)! g_n=\sum_{i=1}^n2·{n\choose i}·g_{n-i}\\ \frac{g_n!}{n!}=\sum_{i=1}^n\frac 2{i!} ·\frac{g_{n-i}}{(n-i)!}
設:
f(x)=i=0gii!xih(x)=i=12i!xi f(x)=\sum_{i=0}^{\infty}\frac{g_i}{i!}x_i\\ h(x)=\sum_{i=1}^{\infty}\frac{2}{i!}x_i
則:
f(x)=f(x)h(x)+1f(x)=11h(x) f(x)=f(x)*h(x)+1\\ f(x)=\frac1{1-h(x)}
爲什麼要加一?因爲 f(x)f(x)g(x)g(x) 卷出來是沒有常數項的。
然後就是多項式求逆啦。

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