组合数学(排列组合,容斥原理,数论定理)

排列组合

一.排列组合基础

1.加法,乘法原理: S= ∑ a i \sum{ai} ai,S= ∏ a i \prod{ai} ai

2.排列: 从n个不同元素中任选m个,组成一个排列,有
A n m A_n^m Anm种可能

3.组合: 从n个不同元素中任选m个,组成一个组合,有
C n m C_n^m Cnm种可能

4.二项式定理:(a+b)n= ∑ i = 0 n \sum_{i=0}^n i=0n an-ibi

5.多重集的排列数:

  • S = ( n 1 ⋅ a 1 , n 2 ⋅ a 2 , … … , n k ⋅ a k ) S = (n_1\cdot a1,n_2\cdot a_2,……,n_k\cdot a_k) S=(n1a1,n2a2,,nkak)
  • S S S 的全排列个数为 n ! ∏ i = 1 k n i ! \frac{n!}{ \prod_{i=1}^k n_i!} i=1kni!n!

6.多重集组合数

  • S = ( n 1 ⋅ a 1 , n 2 ⋅ a 2 , … … , n k ⋅ a k ) S = (n_1\cdot a1,n_2\cdot a_2,……,n_k\cdot a_k) S=(n1a1,n2a2,,nkak)
  • 从中选择 r ( r < = n i ) r(r<=n_i) r(r<=ni) 个,一共有 C r + k − 1 k − 1 C_{r+k-1}^{k-1} Cr+k1k1 种方案。
  • 方法:等价于使用插板法解决 x 1 + x 2 + … … x k = r x_1+x_2+……x_k=r x1+x2+xk=r 的非负整数解的数目

7.不定方程整数解问题

  • 问题1: n n n 个相同的苹果,分给 k k k 个人,求方案数。
  • 解法1: 映射不定方程: x 1 + x 2 + … … + x k = n x_1+x_2+……+x_k=n x1+x2++xk=n。方案数为: C n − 1 k − 1 C_{n-1}^{k-1} Cn1k1
  • 问题2: n n n 个相同的苹果,分给 k k k 个人,每人至少 1 个的方案数。
  • 解法2: 映射不定方程: x i + x 2 + … … + x k = n x_i+x_2+……+x_k=n xi+x2++xk=n。令 y i = x i − 1 , y i > = 0 , y 1 + y 2 + … … + y k = n + k y_i=x_i-1,y_i>=0,y_1+y_2+……+y_k=n+k yi=xi1,yi>=0,y1+y2++yk=n+k。则方案数为 C n + k − 1 k − 1 C_{n+k-1}^{k-1} Cn+k1k1
  • 问题3: n n n 个相同的苹果,分给 k k k 个人,每个人至少 d i d_i di 个的方案数。
  • 解法3: 映射不定方程: x i + x 2 + … … + x k = n x_i+x_2+……+x_k=n xi+x2++xk=n。令 y i = x i − d i , y i > = 0 , y 1 + y 2 + … … + y k = n + ∑ i = 1 k d i y_i=x_i-d_i,y_i>=0,y_1+y_2+……+y_k=n+\sum_{i=1}^kd_i yi=xidi,yi>=0,y1+y2++yk=n+i=1kdi。则方案数为 C n + ∑ d − 1 k − 1 C_{n+\sum d -1}^{k-1} Cn+d1k1

8.错排列

  • 设a为1 - n 的排列,ai != i 的排列的个数
  • 方案数: D n = ( n − 1 ) ( D n − 1 + D n − 2 D_n=(n-1)(D_{n-1}+D_{n-2} Dn=(n1)(Dn1+Dn2)
  • 证明:
  • 运用数学归纳法, D 0 = 1 , D 1 = 0 , D 2 = 1 D_0=1,D_1=0,D_2=1 D0=1,D1=0,D2=1
  • n > 2 n>2 n>2,数字 1 可放在位置 2-n 的任意一个,有 n-1 种可能
  • 假设数字1放在位置2,考虑数字2放置的位置,
  • 若数字2放在位置1,剩余 n-2 个数字错排,即 D n − 2 D_{n-2} Dn2
  • 若数字2不放在位置1,那么数字 3-n 均不能放在对应位置,而数字2也不能放在位置1,刚好就是这n-1个数字错排,即 D n − 1 D_{n-1} Dn1
  • 则: D n = ( n − 1 ) ( D n − 1 + D n − 2 ) D_n=(n-1)(D_{n-1}+D_{n-2}) Dn=(n1)(Dn1+Dn2)

9.圆排列

  • 从n个不同元素中选出m个元素,不分首尾的排成一个圆圈的排列。
  • 其排列方案数: n ! m ( n − m ) ! \frac{n!}{m(n-m)!} m(nm)!n!

二.排列组合练习题

例题1:插空法求不相邻排列

  • 题目描述: 从n个位置中安放 k 个人,求不相邻的排列的方案数
  • 方案数: C n + k − 1 k C_{n+k-1}^k Cn+k1k
  • 问题分析: 从 n 个位置中随便抽取 k 个后,还有 n-k 个位置,n-k 个位置存在 n-k+1 个空。从这 n-k+1 个空中任选 k 个空放入 k 个人,插入这 k 个数,则这 k 个人一定两两不相邻。

例题2:插空法与捆绑法的结合

  • 题目描述: 有n个男生,m个女生,2名老师,他们准备排队。求任意两名女生不相邻且 2 名老师也不相邻的方案数 。
  • 问题分析: 考虑容斥:ans = 任意两名女生不相邻的方案数 - 任意两名女生不相邻且 2 名老师相邻的方案数。前者:插空法,男生和老师一共 n+2 个,一共有 n+3 个空,方案数为 C n + 3 m × A n + 2 n + 2 × A m m C_{n+3}^m\times A_{n+2}^{n+2}\times A_m^m Cn+3m×An+2n+2×Amm 。后者:插空法+捆绑法,先将两名老师看成一共整体,男生和老师一共 n+1 个,一共 n+2 个空,方案数为 C n + 2 m × A n + 1 n + 1 × A m m × A 2 2 C_{n+2}^m\times A_{n+1}^{n+1}\times A_m^m\times A_2^2 Cn+2m×An+1n+1×Amm×A22 。最后答案相减即可。

例题3:插空法

  • 题目描述: x 1 + x 2 + x 3 + x 4 + x 5 + x 6 = n , x > 0 x_1+x_2+x_3+x_4+x_5+x_6=n,x>0 x1+x2+x3+x4+x5+x6=nx>0,则一共有多少 x 1 , x 2 … … x 6 x_1,x_2……x_6 x1,x2x6 的取值。
  • 问题分析: 等价于用五个隔板把 n 个位置隔开,一共有 n-1 个空,并且由于 x>0 ,不同隔板不能在同一个空里,则一共有 C n − 1 5 C_{n-1}^5 Cn15

例题4:组合dp

斯特林数

  • 题目描述: 将n个不同的球放入m个不同的盒子,每个盒子至少放1个球,问有多少种分配方案?
  • 问题分析: f ( n , m ) f(n,m) f(n,m)为将 n 个不同的球放入 m 个盒子中的方案数。考虑第 n 个球放入的位置:若第 n 个球单独放在第 m 个盒子,剩下 n-1 个球放入前 m-1 个盒子中,方案数为 f ( n − 1 , m − 1 ) f(n-1,m-1) f(n1,m1);若剩下 n-1 个球放入前 m 个盒子,则第 n 个球可以随便放在 m 个盒子中的任意一个盒子里,方案数为 f ( n − 1 , m ) × m f(n-1,m)\times m f(n1,m)×m 。由于m个盒子不同,所以还要乘上一个 m 个排列。
#include<bits/stdc++.h>
using namespace std;
long long f(long long n,long long m){
	if(n<0||m<0||n<m)return 0;
	if(n==1&&m==1)return 1;
	return f(n-1,m-1)+f(n-1,m)*m;
}
long long fac(long long m){
	long long ans=1;
	for(int i=1;i<=m;i++)ans=ans*i;
	return ans;
}
int main(){
	long long n,m;
	cin>>n>>m;
	cout<<f(n,m)*fac(m);
	return 0;
}

容斥原理

定理学习

  • 奇加偶减
  • 形式1: A 1 ∪ A 2 ∪ A 3 … … A n − 1 ∪ A n = ∑ ( k 个 元 素 的 交 ) × ( − 1 ) k A_1\cup A_2\cup A_3……A_{n-1}\cup A_n=\sum ( k 个元素的交)\times (-1)^k A1A2A3An1An=(k)×(1)k
  • 形式2: A 1 ∩ A 2 ∩ A 3 … … A n − 1 ∩ A n = U − A ‾ 1 ∪ A ‾ 2 ∪ A ‾ 3 … … A ‾ n − 1 ∪ A ‾ n A_1\cap A_2\cap A_3……A_{n-1}\cap A_n=U-\overline A_1\cup \overline A_2\cup \overline A_3……\overline A_{n-1}\cup \overline A_n A1A2A3An1An=UA1A2A3An1An

假的模板

void solve(){
	long long ans=0,op=1;
	for(int i=n;i>=1;i--){
		ans+=f[i]*op;
		op=-op;
	}
}

例题练习

例题1: [ 1 , n ] [1,n] [1,n] 中有多少个数能被 x 或 y 整除

  • 题目描述: 就是 [ 1 , n ] [1,n] [1,n] 中有多少个数能被 x 或者能被 y 整除
  • 问题分析: 两个的并集 n x + n y \frac{n}{x}+\frac{n}{y} xn+yn。但由于最小公倍数在这两个集合中都被算了一次,需要减去这个交集部分。
  • 答案: n x + n y − n l c m ( x , y ) \frac{n}{x}+\frac{n}{y}-\frac{n}{lcm(x,y)} xn+ynlcm(x,y)n

例题2: [ 1 , n ] [1,n] [1,n] 中有多少个数能被 a i a_i ai 中的任意一个数

  • 题目描述: [ 1 , n ] [1,n] [1,n] 中有多少个数能被 m 个数 a i a_i ai 中的任意一个数整除的数的个数
  • 问题分析: 求所有的交集:容斥板子。计算枚举的数的个数,奇加偶减即可。
  • 写法: 用二进制 01 存储所有 a i a_i ai 的选取情况,去枚举所有的 a i a_i ai 选取情况,再根据容斥定理,选取个数为奇数的需要加上,选取个数为偶数的需要减去。时间复杂度 O ( 2 m ) O(2^m) O(2m)
long long GCD(long long a,long long b){
	if(b==0)return a;;
	else return gcd(b,a%b);
}
long long LCM(long long a,long long b){
	return a/gcd(a,b)*b; 
}
void slove(){
	for(int i=1;i<(1<<m);i++){
		long long op=0,lcm=1;
		for(int j=0;j<m;j++){
			if(i&(1<<j)){
				op++;
				lcm=LCM(lcm,a[j]);
			}
		}
		if(op%2==1)ans+=n/lcm;
		else  ans-=n/lcm;
	}
	cout<<ans; 
}

例题3:k 种颜色对一个一维数组染色,求相邻点颜色不同的方案数

  • 题目描述: n 个连续的点在一维上,k 种颜色。求刚好用了 k 种颜色且相邻点颜色不同的方案数。
  • 问题分析: f [ i ] f[i] f[i] 为用了 < = i <=i <=i 种颜色且相邻的点颜色不同的方案数。对于这 n 个点,第一个点有 i 种可能,之后的每一个点只有 i-1 种可能,而这 i 种颜色从 k 种颜色中取,可能性有 C k i C_k^i Cki 种可能,则 f [ i ] = C k i × i × ( i − 1 ) n − 1 f[i]=C_k^i\times i\times (i-1)^{n-1} f[i]=Cki×i×(i1)n1。很明显:求交,容斥一下即可。
  • 很明显,容斥一下: a n s = ∑ i = 1 k ( − 1 ) k − i × C k i × i × ( i − 1 ) n − 1 ans=\sum_{i=1}^k(-1)^{k-i}\times C_k^i\times i\times (i-1)^{n-1} ans=i=1k(1)ki×Cki×i×(i1)n1

例题4: [ 1 , n ] [1,n] [1,n] 中与 m 互质的数的个数

  • 题目描述: [ 1 , n ] [1,n] [1,n] 中与 m 互质的数的个数。
  • 问题分析: 求与 m 互质,即求与 m 的每个质因子互质,(交集的形式)。容斥一下,求与任意一个质因子不互质的数的个数(这是并集的形式)。与质因子 x 不互质的个数: n − n x n-\frac{n}{x} nxn 。枚举多个质因子时,与多个质因子均不互质的个数: n − n l c m n-\frac{n}{lcm} nlcmn 。然后,奇加偶减即可。(注意: l c m lcm lcm 刚好就等于多个质因子的积)
  • 写法: 同样用二进制 01 去表示是否选取了该因子。时间复杂度 ( O ( m 1 / 2 + 2 k × k ) ) (O(m^{1/2}+2^k\times k)) (O(m1/2+2k×k))
void init(){
	for(int i=2;i*i<=m;i++){
		if(m%i==0){
			factor[num++]=i;
			while(m%i==0)m=m/i;
		} 
	}
	if(m!=1)factor[num++]=m; 
}
void slove(){
	int ans=0;
	for(int i=1;i<(1<<num);i++){
		long long op=0,x=1;
		for(int j=0;j<num;j++){
			if(i&(1<<j)){
				op++;
				x*=factor[j];
			}
		}
		if(op%2==1)ans+=n-n/x;
		else  ans-=n-n/x;
	}
	cout<<n-ans<<endl;
}

例题5:给定四种硬币面值及其数量,求有多少种方式凑成 s

例题链接

  • 题目描述: 给定四个硬币的面值 c i c_i ci 。T 组查询,每次查询给定四个硬币的数量 d i d_i di 和需要凑成的数字 s s s,求有多少种方式从中凑成 s 。 c i , d i , s ∈ [ 1 , 1 e 5 ] , T < = 1000 c_i,d_i,s\in[1,1e5],T<=1000 ci,di,s[1,1e5]T<=1000
  • 问题分析: 看上去就是道多重背包模板题,但是( 1000 × 4 × 1 e 5 × l o g ( 1 e 5 ) ) 1000\times 4\times 1e5 \times log(1e5)) 1000×4×1e5×log(1e5)) 显然超时了。我们先把它当成完全背包看,并求出 f [ j ] f[j] f[j] s = j s=j s=j 时的方案数 。此时由于硬币数量的限制,我们需要去除掉那些不合法的。根据差分的思想:如果我们求出了使用次数在 [ 0 , + ∞ ] [0,+\infty] [0,+] 的方案数和使用次数在 [ d i + 1 , + ∞ ] [d_i+1,+\infty] [di+1,+] 的方案数(即使用次数不合法的方案数),就可以通过做差求出使用次数在 [ 0 , d i ] [0,di] [0,di] 的方案数。考虑第 i 个物品不合法的情况,即第 i 个物品已经使用了 di+1 次,方案数为 f [ s − c i × ( d i + 1 ) ] f[s-ci\times (di+1)] f[sci×(di+1)]。由于有四种硬币,去重会重复的,因此需要容斥一下。
  • 写法: 用二进制枚举,奇加偶减,最后减去即可。
#include<bits/stdc++.h>
using namespace std;

long long T,c[10],d[10],f[100010],s;
int main(){
	for(int i=0;i<4;i++)cin>>c[i];
	f[0]=1;
	for(int i=0;i<4;i++){
		for(int j=c[i];j<=100000;j++){
			f[j]+=f[j-c[i]];
		}
	}
	cin>>T;
	while(T--){
		for(int i=0;i<4;i++)cin>>d[i];
		cin>>s;
		long long t=0;
		for(int i=1;i<(1<<4);i++){
			long long op=0,x=s;
			for(int j=0;j<4;j++){
				if((1<<j)&i){
					op++;
					x-=c[j]*(d[j]+1);
				}
			}
			if(x>=0){
				if(op%2==1)t+=f[x];
				else t-=f[x];
			} 
		}
		cout<<f[s]-t<<endl;
	}
	return 0;
}

例题6:m 个不同的数构成一个长度为 n 的符号码,求前 k 个数至少都使用一次的数符号码方案总数

  • 题目描述: m 个不同的数构成一个长度为 n 的符号码,求前 k 个数至少都使用一次的数符号码方案总数 。
  • 问题分析: 交的形式,先容斥一下变成并。即求前 k 个数字有没有用到数的方案总数。至少有一个数没有用到的方案总数为: C k 1 ( m − 1 ) n C_{k}^1(m-1)^n Ck1(m1)n,至少有 i 个数都没有用到的方案总数为: C k i ( m − i ) n C_{k}^i (m-i)^n Cki(mi)n 。再容斥一下即可。
  • 公式答案:
  • m n − ∑ i = 1 k C k i ( m − i ) n ( − 1 ) i − 1 = ∑ i = 0 k C k i ( m − i ) n ( − 1 ) i m^n-\sum_{i=1}^kC_{k}^i(m-i)^n(-1)^{i-1}=\sum_{i=0}^kC_{k}^i(m-i)^n(-1)^i mni=1kCki(mi)n(1)i1=i=0kCki(mi)n(1)i

卡特兰数

简介

  • 卡特兰数是一种著名数列
  • 其通项公式为:f(n) = C 2 n n n + 1 \frac{C_{2n}^n}{n+1} n+1C2nn
  • 其还有另一种变形:f(n) = C 2 n n C_{2n}^n C2nn - C 2 n n − 1 C_{2n}^{n-1} C2nn1

基本模型

  • 有一个长度为2n的01序列,其中1,0各n个,要求对于任意的前缀中,1的个数不小于0的个数,求满足该条件的序列的个数。

证明
我不会

拓展应用模型
以下模型均符合卡特兰数(我也不知道为什么)

  1. 2n个人排成一排进入剧院,入场费5元,有n个人有5元,有n个人有10元,售票处没有钱找零。求有多少种排队方法,使得不会出现没钱找零的情况。(人长得一样)
  2. 从(0,0)到(n,n),且不穿越(可触碰)直线 y = x 的道路总数。
  3. 圆上有2n个点,求将这些点成对连接起来使得所得到的n条线段不相交的方法数。
  4. 一个无穷大的栈的进栈序列为1,2,3……,n,有多少个不同的出栈序列。
  5. n个结点可构造成多少个不同的二叉树
  6. n个+1和n个-1构成2n项,求其任意前缀均大于等于0的序列的个数。

数论定理

Lucas定理

  • 模型描述: C n m   m o d   p C_{n}^m \bmod p Cnmmodp,当 n,m 过大,但 p 较小时
  • L u c a s ( n , m , p ) = C n ( m o d p ) m ( m o d p ) × L u c a s ( n / p , m / p , p ) Lucas(n,m,p) = C_{n\pmod p}^{m\pmod p} \times Lucas(n/p,m/p,p) Lucas(n,m,p)=Cn(modp)m(modp)×Lucas(n/p,m/p,p)
  • 实现方法:打表,Lucas递归

Code

#include<bits/stdc++.h>
using namespace std;

long long f[100010];
long long ksm(long long a,long long b,long long p){
	long long ans=1;
	while(b){
		if(b&1)ans=ans*a%p; 
		a=a*a%p;
		b=b/2;
	}
	return ans;
}
void init(long long p){
	f[0]=1;
	for(int i=1;i<=p;i++)f[i]=f[i-1]*i%p;
}
long long C(long long n,long long m,long long p){
	if(n<m)return 0;
	return (f[n]*ksm(f[n-m],p-2,p)%p*ksm(f[m],p-2,p)%p);
}
long long Lucas(long long n,long long m,long long p){
	if(m==0)return 1;
	return C(n%p,m%p,p)*Lucas(n/p,m/p,p)%p; 
} 
int main(){
	//init();
	int T;
	long long n,m,p;
	cin>>T;
	while(T--){
		cin>>n>>m>>p;
		init(p);
		cout<<Lucas(n+m,n,p)<<endl;
	}
	return n;
}

Prufer序列

  • 学习目的: P r u f e r Prufer Prufer 序列可以方便的解决一类树相关的计数问题
  • 定理描述: P r u f e r Prufer Prufer 序列与无根树一一对应。一棵 n 个结点的无根树,其对应的 P r u f e r Prufer Prufer 序列长度为 n − 2 n-2 n2
  • 性质1: n n n 个结点的完全图的生成树一共有 n n − 2 n^{n-2} nn2。解释: p r u f e r prufer prufer 每个位置均有 n n n 种可能。
  • 性质2: 度数为 d i d_i di 的节点会在 p u r f e r purfer purfer 序列中出现 d i − 1 d_i-1 di1 次。
  • 性质3: 对于给定度数为 d 1 ∼ n d_{1\sim n} d1n 的无根树共有 ( n − 2 ) ! ∏ i = 1 n ( d i − 1 ) ! \frac{(n-2)!}{\prod_{i=1}^n(d_i-1)!} i=1n(di1)!(n2)! 种情况。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值