二项式反演与斯特林反演

二项式反演

如果 f ( m ) = ∑ i = m n ( n i ) g ( i ) f(m)=\sum_{i=m}^n{n\choose i}g(i) f(m)=i=mn(in)g(i)
根据二项式反演:
g ( i ) = ∑ k = i n ( − 1 ) k − i ( k i ) f ( k ) g(i)=\sum_{k=i}^n(-1)^{k-i}{k\choose i}f(k) g(i)=k=in(1)ki(ik)f(k)
另一种形式罢了。
正常版本:
f ( n ) = ∑ i = 0 n ( n i ) g ( i ) g ( n ) = ∑ i = 0 n ( − 1 ) n − i ( n i ) 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(in)g(i)g(n)=i=0n(1)ni(in)f(i)
f ( n ) = ∑ i = 0 n ( − 1 ) i ( n i ) g ( i ) g ( n ) = ∑ i = 0 n ( − 1 ) i ( n i ) 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)\\ f(n)=i=0n(1)i(in)g(i)g(n)=i=0n(1)i(in)f(i)

就是在容斥。例如:

1

n 个人枪手,每名枪手随机瞄准其余的 n-1 个人中的一人,有 p 的概率命中。问同时打一枪后,恰有 i 个人死的概率。

f ( S ) f(S) f(S) 表示恰为 S 死的概率, g ( S ) g(S) g(S) 表示死的是 S 子集的概率。那么显然 g g g 可以快速求出(每个人都向某个集合瞄准),并且 g ( S ) = ∑ T ⊆ S f ( T ) g(S) = \sum_{T\subseteq S} f(T) g(S)=TSf(T),那么容斥容易得到 f ( S ) = ∑ T ⊆ S ( − 1 ) ∣ S ∣ − ∣ T ∣ g ( T ) f(S) = \sum_{T\subseteq S} (-1)^{|S|-|T|}g(T) f(S)=TS(1)STg(T)

发现对于 |S| 相同的集合, f ( S ) f(S) f(S) 也一样。于是用 |S| 代替 S,上述两个式子就变成了 g ( n ) = ∑ i = 0 n ( n i ) f ( i ) g(n) = \sum_{i = 0}^n {n \choose i}f(i) g(n)=i=0n(in)f(i) f ( n ) = ∑ i = 0 n ( − 1 ) n − i ( n i ) g ( i ) f(n) = \sum_{i = 0}^n (-1)^{n-i}{n \choose i}g(i) f(n)=i=0n(1)ni(in)g(i),和二项式定理是一样的。

斯特林数

  • 第一类斯特林数:把n个标号元素排成m个环的方案数。
    S 1 ( n , m ) = S 1 ( n − 1 , m − 1 ) + ( n − 1 ) ⋅ S 1 ( n − 1 , m ) S_1(n,m)=S_1(n-1,m-1)+(n-1)\cdot S_1(n-1,m) S1(n,m)=S1(n1,m1)+(n1)S1(n1,m)有一种组合意义是 n n n 个元素的排列,有 m m m 个前缀最大值的方案数。可以自然推出: ∑ i = 0 n S 1 ( n , i ) = n ! \sum_{i=0}^nS_1(n,i)=n! i=0nS1(n,i)=n!
    根据递推式还可以写出 S 1 ( n ) S_1(n) S1(n) 的生成函数: S 1 ( n ) = ∏ i = 0 n − 1 ( x + i ) S_1(n)=\prod_{i=0}^{n-1} (x+i) S1(n)=i=0n1(x+i)。我们用分治 FFT 在 nlog2n 的时间计算某一行。

  • 第二类斯特林数:把n个标号元素划分为m个相同集合的方案数。
    S 2 ( n , m ) = S 2 ( n − 1 , m − 1 ) + m ⋅ S 2 ( n − 1 , m ) S_2(n,m)=S_2(n-1,m-1)+m\cdot S_2(n-1,m) S2(n,m)=S2(n1,m1)+mS2(n1,m)
    容斥计算:
    S 2 ( n , m ) = 1 m ! ∑ i = 0 m ( − 1 ) i ( m i ) ( m − i ) n = ∑ i = 0 m ( − 1 ) i i   ! ⋅ ( m − i ) n ( m − i ) ! 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)!} S2(n,m)=m!1i=0m(1)i(im)(mi)n=i=0mi !(1)i(mi)!(mi)n可以O(n)算某一项,nlogn 计算某一行。

小技巧

n k = ∑ i = 0 n S 2 ( k , i ) ( n i ) i ! n^k=\sum_{i=0}^nS_2(k,i){n\choose i}i! nk=i=0nS2(k,i)(in)i!
左边相当于把k个标号球放进n个标号盒子里。右边枚举放在那些盒子里,由于盒子有标号所以乘上阶乘,再乘上斯特林数。

注意到上式的 i 枚举到 n,实际上可以枚举到 k,因为后面的组合数乘阶乘可以看做一个 i 次下降幂,如果 k>n 那么这个下降幂会变成 0,如果 k<n,那么 i 在 大于 k 的时候前面的斯特林数就会变成 0。也就是:
n k = ∑ i = 0 n S 2 ( k , i ) ⋅ n i ↓ n^k=\sum_{i=0}^nS_2(k,i)\cdot n^{i\downarrow} nk=i=0nS2(k,i)ni
我们把这个式子变得和底数没什么关系了。有什么好处呢?如果我们要维护 ∑ a i k \sum a_i^k aik,k 是常数,我们只需要维护 ∑ a i k ↓ \sum a_i^{k\downarrow} aik,最后再 O(k) 计算答案。注意到 ( x + 1 ) k ↓ = x k ↓ + k ⋅ x ( k − 1 ) ↓ (x+1)^{k\downarrow}= x^{k\downarrow}+k·x^{(k-1)\downarrow} (x+1)k=xk+kx(k1)(考虑组合意义),因此 ∑ a i k ↓ \sum a_i^{k\downarrow} aik 这个式子是可以在 O(k) 的时间变成 ∑ ( a i + 1 ) k ↓ \sum (a_i+1)^{k\downarrow} (ai+1)k

2

给 n, k 和一个长度为 n 的数字串。要求对每一个 i, 输出 ∑ j = 1 i F ( j , i ) ∑_{j=1}^i F(j, i) j=1iF(j,i)。其中 F(l, r) 为 l 到 r 内所有数字之和的 k 次方。
n ≤ 50000 , k ≤ 100 n\leq 50000,k\leq 100 n50000,k100

直接二项式定理复杂度是 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 = 1 n S 2 ( n , i ) ⋅ g [ i ] g [ n ] = ∑ i = 1 n ( − 1 ) n − i ⋅ S 1 ( 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] f[n]=i=1nS2(n,i)g[i]g[n]=i=1n(1)niS1(n,i)f[i]
f [ m ] = ∑ i = m n S 2 ( i , m ) ⋅ g [ i ] g [ m ] = ∑ i = m n ( − 1 ) i − m S 1 ( i , m ) ⋅ f [ i ] f[m]=\sum_{i=m}^nS_2(i,m)\cdot g[i]\\ g[m]=\sum_{i=m}^n(-1)^{i-m}S_1(i,m)\cdot f[i] f[m]=i=mnS2(i,m)g[i]g[m]=i=mn(1)imS1(i,m)f[i]
相比二项式反演就是把组合数换成斯特林数。

3

定义两个结点数相同的图 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 = m n S 2 ( i , m ) ⋅ g [ i ] f[m]=\sum_{i=m}^nS_2(i,m)\cdot g[i] f[m]=i=mnS2(i,m)g[i]
斯特林反演一下:
g [ m ] = ∑ i = m n ( − 1 ) i − m S 1 ( i , m ) ⋅ f [ i ] g[m]=\sum_{i=m}^n(-1)^{i-m}S_1(i,m)\cdot f[i] g[m]=i=mn(1)imS1(i,m)f[i]
只要求g[1],即
g [ 1 ] = ∑ i = 1 n ( − 1 ) i − 1 S 1 ( i , 1 ) ⋅ f [ i ] = ∑ i = 1 n ( − 1 ) i − 1 ( i − 1 ) ! ⋅ 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] g[1]=i=1n(1)i1S1(i,1)f[i]=i=1n(1)i1(i1)!f[i]

那么问题就变成了如何求f。我们把边压成一个longlong,枚举了集合划分之后,相当于有一些数,求有多少个子集异或位0。根据线性基的理论,我们求出一个线性基,枚举线性基外面的任意一个集合,都能在线性基里找到恰好一个元素,它们的异或为0。

4

雅礼集训 方阵

给定一个方阵,每个位置可以填 [1,c] 中的任意一个数,要求填完之后不能有两行或两列完全相同,求方案数。 n , m ≤ 5000 n,m\leq 5000 n,m5000

这种行列都有限制的题目往往是先考虑一边,也就是让行都不等价,这样列可能存在等价的情况。设 f_n 表示 n 列互补等价的方案数,g_n 表示任意的方案数,那么实际上 g n = ∑ i = 1 n f i S 2 ( n , i ) g_n=\sum_{i=1}^nf_iS_2(n,i) gn=i=1nfiS2(n,i),反演一下可以得到 f n = ∑ i = 1 n ( − 1 ) n − i g i S 1 ( n , i ) f_n=\sum_{i=1}^n(-1)^{n-i}g_iS_1(n,i) fn=i=1n(1)nigiS1(n,i)

容易发现,第二类斯特林数总是和等价类划分之类的问题相关。

5

计算
∑ i = 0 n ∑ j = 0 n S ( i , j ) ⋅ j ! ⋅ 2 j \sum_{i=0}^n\sum_{j=0}^nS(i,j)·j!·2^j i=0nj=0nS(i,j)j!2j

解法1:
第二类斯特林数可以展开:
S ( n , m ) = 1 m ! ∑ i = 0 m ( − 1 ) i ⋅ m ! i ! ( m − i ) ! ⋅ ( m − i ) n = ∑ i = 0 m ( − 1 ) i i   ! ⋅ ( m − i ) n ( m − i ) ! 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)!} S(n,m)=m!1i=0m(1)ii!(mi)!m!(mi)n=i=0mi !(1)i(mi)!(mi)n
是一个卷积。因此我们可以在 O ( n log ⁡ n ) O(n\log n) O(nlogn) 的时间里计算 S ( n , 0 ) , S ( n , 1 ) , … , S ( n , n ) S(n,0),S(n,1),\dots,S(n,n) S(n,0),S(n,1),,S(n,n)

代回原式:
∑ i = 0 n ∑ j = 0 n S ( i , j ) ⋅ j ! ⋅ 2 j = ∑ i = 0 n ∑ j = 0 n j ! ⋅ 2 j ∑ k = 0 j ( − 1 ) k k   ! ⋅ ( j − k ) i ( j − k ) ! = ∑ j = 0 n j ! ⋅ 2 j ∑ k = 0 j ( − 1 ) k k   ! ⋅ ∑ i = 0 n ( j − k ) i ( j − k ) ! \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)!} i=0nj=0nS(i,j)j!2j=i=0nj=0nj!2jk=0jk !(1)k(jk)!(jk)i=j=0nj!2jk=0jk !(1)k(jk)!i=0n(jk)i

解法2:
考虑第二类斯特林数的意义,是把 n n n 个不同的球放入 m m m 个相同盒子里的方案数。由于盒子非空,因此乘上一个 j ! j! j! 以后就是放进不同盒子的方案数了。
我们令 g n g_n gn 表示 ∑ i = 0 n S ( n , i ) ⋅ i ! ⋅ 2 i \sum_{i=0}^nS(n,i)·i!·2^i i=0nS(n,i)i!2i。如果不考虑 2 i 2^i 2i,也就是把 n n n 个不同的球放进任意多个不同的盒子里的方案数。我们枚举最后一个盒子放几个球,可以写出递推式:
g n = ∑ i = 1 n 2 ⋅ ( n i ) ⋅ g n − i g n ! n ! = ∑ i = 1 n 2 i ! ⋅ g n − i ( n − i ) ! 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)!} gn=i=1n2(in)gnin!gn!=i=1ni!2(ni)!gni
设:
f ( x ) = ∑ i = 0 ∞ g i i ! x i h ( x ) = ∑ i = 1 ∞ 2 i ! x i 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)=i=0i!gixih(x)=i=1i!2xi
则:
f ( x ) = f ( x ) ∗ h ( x ) + 1 f ( x ) = 1 1 − h ( x ) f(x)=f(x)*h(x)+1\\ f(x)=\frac1{1-h(x)} f(x)=f(x)h(x)+1f(x)=1h(x)1
为什么要加一?因为 f ( x ) f(x) f(x) g ( x ) g(x) g(x) 卷出来是没有常数项的。
然后就是多项式求逆啦。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值