模板:组合数学

16 篇文章 1 订阅
本文深入探讨了组合数学的多个关键概念,包括斯特林数的两类及其递推公式,小球入盒问题,排列与组合的生成方法,位运算在组合计数中的应用,以及一些特殊数列如Catalan数和Bell数的性质。此外,还介绍了如何使用扩展Cayley公式解决连通块问题,以及在图形理论中的应用。
摘要由CSDN通过智能技术生成

组合数学

组合数取模

为方便,记 C ( n , m ) = C n m = ( n m ) C(n,m)=C_n^m=\binom{n}{m} C(n,m)=Cnm=(mn)

struct Factorial//预处理阶乘及对应的逆元
{
	vector<ll> fac,ifac;
	ll M;
	Factorial(int N,ll M):fac(N,1),ifac(N,1),M(M)
	{
		for(int i=2; i<N; ++i)
			fac[i]=mul(fac[i-1],i,M),ifac[i]=mul(M-M/i,ifac[M%i],M);
		for(int i=2; i<N; ++i)ifac[i]=mul(ifac[i],ifac[i-1],M);
	}
	ll c(int n,int m)
	{
		return mul(mul(fac[n],ifac[m],M),ifac[n-m],M);
	}
	ll lucas(ll n,ll m)//卢卡斯定理求C(n,m)%M,适用于模数M小于N的情况,或者m较小的时候也可以暴力求
	{
		return !m?1:
		       n<m||n%M<m%M?0:
		       n<M&&m<M?c(n,m):
		       mul(lucas(n/M,m/M),lucas(n%M,m%M),M);
	}
};

组合数lcm

( n + 1 ) l c m ( C ( n , 0 ) , C ( n , 1 ) , … , C ( n , k ) ) = l c m ( n + 1 , n , n − 1 , n − 2 , … , n − k + 1 ) (n + 1)lcm(C(n,0),C(n,1),\ldots,C(n,k))=lcm(n+1,n,n−1,n−2,\ldots,n−k+1) (n+1)lcm(C(n,0),C(n,1),,C(n,k))=lcm(n+1,n,n1,n2,,nk+1)

区间lcm的维护

对于一个数,将其分解质因数,若有因子 p k p^k pk,那么拆分出k个数 p 1 , p 2 , … , p k p^1,p^2,\ldots,p^k p1,p2,,pk,权值都为p,那么区间 [ l , r ] [l,r] [l,r]内所有数的lcm的答案=所有在该区间中出现过的数的权值之积,可持久化线段树维护之。

Stirling数

第一类斯特林数

第一类斯特林数 S ( p , k ) S(p,k) S(p,k)的一个的组合学解释是:将p个物体排成k个非空循环排列的方法数。
递推公式: S ( p , k ) = ( p − 1 ) S ( p − 1 , k ) + S ( p − 1 , k − 1 ) , 1 ≤ k ≤ p − 1 ; S ( p , 0 ) = 0 , p ≥ 1 ; S ( p , p ) = 1 , p ≥ 0 S(p,k) = (p−1)S(p−1,k)+S(p−1,k−1),1\leq k\leq p−1;S(p,0) = 0,p\ge1;S(p,p) = 1,p\ge0 S(p,k)=(p1)S(p1,k)+S(p1,k1),1kp1;S(p,0)=0,p1;S(p,p)=1,p0

第二类斯特林数

第二类斯特林数 S ( p , k ) S(p,k) S(p,k)的一个的组合学解释是:将p个物体划分成k个非空不可辨别的(可以理解为盒子没有编号)集合的方法数。
递推公式: S ( p , k ) = k S ( p − 1 , k ) + S ( p − 1 , k − 1 ) , 1 ≤ k ≤ p − 1 ; S ( p , 0 ) = 0 , p ≥ 1 ; S ( p , p ) = 1 , p ≥ 0 S(p,k) = kS(p−1,k) + S(p−1,k−1),1\leq k\leq p−1;S(p,0) = 0,p\ge 1;S(p,p) = 1,p\ge0 S(p,k)=kS(p1,k)+S(p1,k1),1kp1;S(p,0)=0,p1;S(p,p)=1,p0
卷积形式: S ( n , m ) = 1 m ! ∑ k = 0 m ( − 1 ) k C ( m , k ) ( m − k ) n = ∑ k = 0 m ( − 1 ) k k ! ( m − k ) n ( m − k ) ! S(n,m)=\frac{1}{m!}\sum_{k=0}^m (-1)^kC(m,k)(m-k)^n=\sum_{k=0}^m\frac{(-1)^k}{k!}\frac{(m-k)^n}{(m-k)!} S(n,m)=m!1k=0m(1)kC(m,k)(mk)n=k=0mk!(1)k(mk)!(mk)n
同时有转化: x k = ∑ i = 1 k i ! C ( x , i ) S ( k , i ) x^k=\sum_{i=1}^ki!C(x,i)S(k,i) xk=i=1ki!C(x,i)S(k,i)

斯特林近似公式

n ! ≈ 2 π n ( n e ) n n!\approx\sqrt{2πn}(\frac{n}{e})^n n!2πn (en)n

小球入盒问题

k 个球m个盒子空盒子方案数
各不相同各不相同允许 m k m^k mk
各不相同各不相同 m ! S t i r l i n g 2 ( k , m ) m!Stirling2(k,m) m!Stirling2(k,m)
各不相同完全相同允许 ∑ i = 1 m S t i r l i n g 2 ( k , i ) \sum_{i=1}^mStirling2(k,i) i=1mStirling2(k,i)
各不相同完全相同 S t i r l i n g 2 ( k , m ) Stirling2(k,m) Stirling2(k,m)
完全相同各不相同允许 C ( m + k − 1 , k ) C(m+k−1,k) C(m+k1,k)
完全相同各不相同 C ( k − 1 , m − 1 ) C(k−1,m−1) C(k1,m1)
完全相同完全相同允许 1 ( 1 − x ) ( 1 − x 2 ) . . . ( 1 − x m ) 的 x k 项 的 系 数 \frac{1}{(1−x)(1−x^2)...(1−x^m)}的x^k项的系数 (1x)(1x2)...(1xm)1xk
完全相同完全相同 x m ( 1 − x ) ( 1 − x 2 ) . . . ( 1 − x m ) 的 x k 项 的 系 数 \frac{x^m}{(1−x)(1−x^2)...(1−x^m)}的x^k项的系数 (1x)(1x2)...(1xm)xmxk

置换

struct Permutation:vector<int>
{
	Permutation(int n=0):vector<int>(n) {}
	friend Permutation operator*(const Permutation &f,const Permutation &g)
	{
		Permutation ans(f.size());
		for(int i=0; i<f.size(); ++i)ans[i]=g[f[i]];
		return ans;
	}
	friend Permutation inv(const Permutation &f)
	{
		Permutation ans(f.size());
		for(int i=0; i<f.size(); ++i)ans[f[i]]=i;
		return ans;
	}
	friend vector<vector<int> > cycle(const Permutation &f)
	{
		vector<int> vis(f.size(),0);
		vector<vector<int> > ans;
		for(int i=0; i<f.size(); ++i)
			if(!vis[i])
			{
				ans.push_back(vector<int>());
				for(int j=i; !vis[j]; j=f[j])
				{
					vis[j]=1;
					ans.back().push_back(j);
				}
			}
		return ans;
	}
};

生成下一字典序

生成排列

对给定的排列 a 1 a 2 … a n a_1a_2\ldots a_n a1a2an,找到 a j a_j aj使得 a j &lt; a j + 1 , a j + 1 &gt; a j + 2 &gt; … &gt; a n a_j&lt;a_{j+1},a_{j+1}&gt;a_{j+2}&gt;\ldots&gt;a_n aj<aj+1,aj+1>aj+2>>an即这列数中最后一个相邻递增数对,然后把 a j + 1 , a j + 2 , … , a n a_{j+1},a_{j+2},\ldots,a_n aj+1,aj+2,,an中大于 a j a_j aj的最小数放到位置j,然后 a j … a n a_j\ldots a_n ajan中剩余的数从小到大排序放到 [ j + 1 , n ] [j+1,n] [j+1,n]中。

bool nextPermutation(ll *b,ll *e)//标准库有这个函数next_permutation
{
	ll *i=e-1,*j=e-2;
	while(j>=b&&*j>=*(j+1))--j;
	if(j<b)return 0;
	while(*i<=*j)--i;
	return swap(*i,*j),reverse(j+1,e),1;
}

生成组合

//待填坑

位运算应用

生成所有非空子集

枚举i的非空子集j。

for(j=i; j; j=i&j−1) {}

gcc内置位运算

这些函数都有相应的usigned longusigned long long版本,只需要在函数名后面加上lll就可以了,比如__builtin_clzll.

int __builtin_clz(unsigned int x);//求前缀0的个数
int __builtin_ctz(unsigned int x);//求后缀0的个数
int __builtin_ffs(unsigned int x);//x的二进制末尾最后一个1的位置,从1开始
int __builtin_popcount(unsigned int x);//x二进制中1的个数,相当于bitset::count()
int __builtin_parity(unsigned int x);//判断x的二进制中1的个数的奇(1)偶(0)性

二项式反演

f ( n ) = ∑ k = 0 n C ( n , k ) g ( k ) , g ( n ) = ∑ k = 0 n ( − 1 ) n − k C ( n , k ) f ( k ) f(n)=\sum_{k=0}^nC(n,k)g(k),g(n)=\sum_{k=0}^n(−1)^{n−k}C(n,k)f(k) f(n)=k=0nC(n,k)g(k),g(n)=k=0n(1)nkC(n,k)f(k)

第k小期望

f ( n , k ) f(n,k) f(n,k) 表示有n个变量,和为1,第k小的期望。
f ( n , k ) = 1 n 2 + ( 1 − 1 n ) f ( n − 1 , k − 1 ) , f ( n , 0 ) = 0 f(n,k)=\frac{1}{n^2}+(1-\frac{1}{n})f(n-1,k-1),f(n,0)=0 f(n,k)=n21+(1n1)f(n1,k1),f(n,0)=0

错排数

考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。
n个元素的错排数 D n D_n Dn满足递推公式: D 1 = 0 , D 2 = 1 , D n = ( n − 1 ) ( D n − 2 + D n − 1 ) D_1=0,D_2=1,D_n=(n−1)(D_{n−2}+D_{n−1}) D1=0,D2=1,Dn=(n1)(Dn2+Dn1)
通项: D ( n ) = n ! [ ( − 1 ) 2 2 ! + … + ( − 1 ) n − 1 ( n − 1 ) ! + ( − 1 ) n n ! ] = ⌊ n ! e + 1 2 ⌋ D(n)=n![\frac{(-1)^2}{2!}+\ldots+\frac{(-1)^{n-1}}{(n-1)!}+\frac{(-1)^n}{n!}]=\lfloor\frac{n!}{e}+\frac{1}{2}\rfloor D(n)=n![2!(1)2++(n1)!(1)n1+n!(1)n]=en!+21

Bonuli数

B n = − 1 C ( n + 1 , n ) ( C ( n + 1 , 0 ) B 0 + C ( n + 1 , 1 ) B 1 + … + C ( n + 1 , n − 1 ) B n − 1 ) = − 1 n + 1 ( C ( n + 1 , 0 ) B 0 + C ( n + 1 , 1 ) B 1 + … + C ( n + 1 , n − 1 ) B n − 1 ) B_n = -\frac{1}{C(n+1,n)}(C(n+1,0)B_0+C(n+1,1)B_1+\ldots+C(n+1,n-1)B_{n-1})=-\frac{1}{n+1}(C(n+1,0)B_0+C(n+1,1)B_1+\ldots+C(n+1,n-1)B_{n-1}) Bn=C(n+1,n)1(C(n+1,0)B0+C(n+1,1)B1++C(n+1,n1)Bn1)=n+11(C(n+1,0)B0+C(n+1,1)B1++C(n+1,n1)Bn1)
可用于计算任意正整数次数的幂和: ∑ i = 1 n i k = 1 k + 1 ∑ j = 0 k C ( k + 1 , j ) B j n k + 1 − j \sum_{i=1}^ni^k=\frac{1}{k+1}\sum_{j=0}^kC(k+1,j)B_jn^{k+1-j} i=1nik=k+11j=0kC(k+1,j)Bjnk+1j

struct Bonuli:Factorial
{
	vector<ll> b;
	Bonuli(int N,ll M):Factorial(N,M),b(N,0)
	{
		for(int i=b[0]=1; i<N; ++i)
		{
			for(int j=0; j<i; ++j)b[i]=(b[i]+mul(b[j],c(i+1,j),M))%M;
			b[i]=(M-mul(mul(fac[i],ifac[i+1],M),b[i],M))%M;
		}
	}
	ll ask(ll n,int k)//return $\sum_{i=1}^ni^k\mod M$
	{
		ll r=0,w=1,u=(n+1)%M;
		for(int i=1; i<=k+1; ++i)
			r=(r+mul(mul(b[k+1-i],c(k+1,i),M),w=mul(w,u,M),M))%M;
		return mul(mul(fac[k],ifac[k+1],M),r,M);
	}
};

Catalan数

h 1 = 1 , h n = 4 n − 2 n + 1 h n − 1 = C ( 2 n , n ) n + 1 = C ( 2 n , n ) − C ( 2 n , n − 1 ) h_1 = 1,h_n =\frac{4n−2}{n+1}h_{n−1} =\frac{C(2n,n)}{ n+1 }= C(2n,n)−C(2n,n−1) h1=1,hn=n+14n2hn1=n+1C(2n,n)=C(2n,n)C(2n,n1)
在一个格点阵列中,从 ( 0 , 0 ) (0,0) (0,0)点走到 ( n , m ) (n,m) (n,m)点且不经过对角线 x = y x=y x=y的方法数:$ C(n + m−1,m)−C(n + m−1,m−1),x > y;C(n + m,m)−C(n + m,m−1),x\ge y$。

Bell数

把n个带标号的物品划分为若干不相交集合的方案数称为贝尔数,其递推公式: B n = ∑ i = 0 N − 1 C n − 1 i B i B_n=\sum_{i=0}^{N-1}C_{n-1}^iB_i Bn=i=0N1Cn1iBi
前几项贝尔数: 1 , 2 , 5 , 15 , 52 , 203 , 877 , 4140 , 21147 , 115975 , 678570 , 4213597 , 27644437 , 190899322 , 1382958545 , … 1,2,5,15,52,203,877,4140,21147,115975,678570,4213597,27644437,190899322,1382958545,\ldots 1,2,5,15,52,203,877,4140,21147,115975,678570,4213597,27644437,190899322,1382958545,
下为 O ( P 2 log ⁡ P ) O(P^2\log P) O(P2logP) B n B_n Bn对P取模。

struct Bell {
	static const int P=999999598,N=7284;
	int a[4],f[N],s[2][N],i,j,x;
	Bell() {
		a[0]=2,a[1]=13,a[2]=5281,a[3]=7283;//P的质因数分解
		f[0]=f[1]=s[0][0]=1,s[0][1]=2;
		for(i=2,x=1; i<N; i++,x^=1)
			for(f[i]=s[x][0]=s[x^1][i-1],j=1; j<=i; ++j)
				s[x][j]=(s[x^1][j-1]+s[x][j-1])%P;
	}
	int cal(int x,ll n) {
		int i,j,k,m=0,b[N],c[N],d[70];
		for(i=0; i<=x; i++)b[i]=f[i]%x;
		while(n)d[m++]=n%x,n/=x;
		for(i=1; i<m; i++)for(j=1; j<=d[i]; j++) {
				for(k=0; k<x; k++)c[k]=(b[k]*i+b[k+1])%x;
				c[x]=(c[0]+c[1])%x;
				for(k=0; k<=x; k++)b[k]=c[k];
			}
		return c[d[0]];
	}
	ll ask(ll n) {
		if(n<N)return f[n];
		ll t=0;
		for(int i=0; i<4; ++i)t=(t+(P/a[i])*pow(P/a[i],a[i]-2,a[i])%P*cal(a[i],n)%P)%P;
		return t;
	}
};

等价类容斥

考虑容斥,Bell§枚举所有等价情况。对于一种情况,强制了一个等价类里面的数都要相同,其它的可以相同也可以不同。
容斥系数为: ( − 1 ) p − 等 价 类 个 数 ( 每 个 等 价 类 大 小 − 1 ) ! 之 积 (−1)^{p−等价类个数}(每个等价类大小−1)!之积 (1)p(1)!

Grey码

格雷序列第i个是i^(i>>1)。长为n的01序列共 2 n 2^n 2n个,下标从 0 … 2 n − 1 0\ldots 2^n-1 02n1

扩展Cayley公式

对于n个点,m个连通块的图,假设每个连通块有a[i]个点,那么用s−1条边把它连通的方案数为 n s − 2 a [ 1 ] a [ 2 ] . . . a [ m ] n^{s−2}a[1]a[2]...a[m] ns2a[1]a[2]...a[m]

超立方体

n维超立方体有 2 n − i C ( n , i ) 2^{n−i}C(n,i) 2niC(n,i) 个i维元素。

有根树森林

固定k个点作为根的n个点的带标号有根树森林的方案数是 n n − k − 1 k n^{n−k−1}k nnk1k

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值