排列组合

闲言

昨天晚上在宿舍睡觉的时候,问 \(hxk\) ,你不会不组合数学,他说会,我说:速教我。他 \(TM\) 说,你也配, \(MD\) , 这个 \(ZZ\) ,

入门

我相信,学排列组合的话,高中选择性必修二很显然是不错的。

加法 & 乘法原理

  • 加法原理
    在完成一件事情有 \(n\) 种方法,\(a_i\) 表示第 \(i\) 中方法的数目,那么总方法就有:\(S = \sum_{i = 1}^{n} a_i\)
  • 乘法原理
    在完成一件事情有 \(n\) 个步骤, \(a_i\) 表示第 \(i\) 个步骤的不同方法的数目,则总方法有:
    \(S = \prod_{i=1}^{n}a_i\)

排列数

\(n\) 个不同的元素中依次取出 \(m\) 个元素排成一列,产生的不同排列的数目为 :
\(A_{n}^{m} = \frac{n!}{(n-m)!} = n \times (n - 1) \times … \times (n - m + 1)\)

证明:我依稀记得吧, 当选择第 \(1\) 个元素的时候,我们有 \(n\) 的数目, \(2\) 个元素的时候,有 \(n - 1\) 的数目,一直到 \(m\) 个元素的时候,我们用 \(n - m + 1\) 的数目选择。根据乘法原理,就可以知道了。

组合数

\(n\) 个不同元素中取出 \(m\) 个数组成一个集合(不考虑顺序) , 产生的不同集合数目为:
\(C_{n}^{m} = \frac{n!}{m!(n - m)!} = \frac{n \times (n - 1) \times … \times (n - m + 1)}{m\times (m - 1)\times … \times 2\times 1}\)

如何理解上述式子 : 选出 \(m\) 个人来,不排队,不在乎顺序。如果在乎顺序的话,就是 \(A_{n}^{m}\) , 那么重复了 \(m\)了,同样让 \(m\) 个人排列,保证最后是不重复的。

现在数学中常用 : \(\displaystyle \binom{n}{m}\) 代替 \(C_{n} ^{m}\)

性质 :

  • \(1. \ \ C_{n}^{m} = C_{n}^{n - m}\)

由组合数的定义,对于从 \(n\) 个不同元素中取出 \(m\) 个组成的每个集合,剩余未取出的元素也构成一节集合,两个集合一一对应,所以性质1成立 。

  • $2. \ \ C_{n}^{m} = C_{n - 1}^{m} + C_{n - 1}^{m - 1} $

\(n\) 个不同元素中取出 \(m\) 个组成的一个集合有两种方法:取 \(n\)浩元素,不取 \(n\) 号元素,若取 \(n\) 号元素,则应在剩余的 \(n - 1\) 个中选择 \(m - 1\) , 则方案数为 \(C_{n - 1}^{m - 1}\) , 若不取 \(n\) 号元素,则应在剩余的 \(n - 1\) 个数中选择 \(m\) 个数,即为 \(C_{n - 1}^{m}\) , 综上,性质二成立 。

  • \(3. \ \ \sum_{i = 0}^{n}C_{n}^{i} = 2^n\)

\(n\) 个元素中取出若干个元素组成一个集合,有 \(n + 1\) 中方法 ,分别是取出 \(0 , 1 , 2 … n\) 个。 根绝加法原理,共有 \(C_{n}^{0} + C_{n}^{1} + C_{n}^{2} … + C_{n}^{n}\) 。 同样的,每一个元素都对应着选或者不选,那么根据乘法原理,就得知 \(2^n\) ,都是代表的同样的,所以综上,性质三成立

求解组合数的方法 :

  • \(1.\) 根据性质二, 我们很显然的可以得出一种思路,那就递推求解 , 递推式是杨辉三角的递推式 \(f_{i,j} = f_{i-1,j} + f_{i-1 , j-1}\) , 但是这种的复杂度是 \(O(n^2)\) 的,很显然不是那么优秀。
  • 一般由于组合数都比较大,从而要求 \(mod\) ,这种时候,我们就可以 $n! \ \ mod \ \ $ , 然后又因为 \(1 \text{~} p\) 满足存在逆元 ,其 对 \(/(n - m) !\) 就直接用逆元解决掉。,进行一下预处理就好。

大概是: \(C_{n}^{m} \text{%} p= f_n \times finv_{m} \times finv_{n-m}\)

关于求解逆元的方法的方法,这里就不再赘述了。

二次项定理

\[(a + b) ^ n = \sum_{i = 0}^{n} \binom{n}{i}a^{n - i} b^i \]

证明:咕了。

例题 : 计算系数 Code

进阶

略微难一些。

Lucas 定理

\(p\) 为质数,则对于任意整数 \(1 \leq m \leq n\) 有:

\[\displaystyle C_{n}^{m} = C_{n \text{%} p}^{m \text{%} p} \times C_{n / p}^{m / p} (mod \ \ p) \]

如何理解这个式子

  • 就是把 \(n,m\) 全部转化成 \(p\) 进制数,对 \(p\) 进制下的每一位分别记录组合数,最后再乘起来。
  • 证明的话,一般用到生成函数知识,不涉及。
inline void init() 
{
	f[0] = 1 ; 
	for(qwq int i = 1 ; i <= p ; i++) f[i] = (f[i - 1] * i) % p ;
}
inline int powe(int k , int b , int mod) 
{
	int ret = 1 ; 
	while(b) 
	{
		if(b & 1) ret = ret * k % mod ; 
		k = k * k % mod; 
		b >>= 1 ; 
	}
	return ret % mod; 
}
inline int calc(int n , int m) 
{
	if(m > n) return 0 ; 
	return (( f[n] * powe(f[m] , p - 2 , p) ) % p * powe(f[n - m] , p - 2 , p) ) % p ;  
}
inline int Lucas(int n , int m)
{
	if(!m) return 1 ; 
	return calc(n % p , m % p) * Lucas(n / p , m / p ) % p ;
}
signed main()
{
	int T = read() ; 
	while(T--) 
	{
		n = read() , m = read() , p = read() ; 
		init() ; 
		printf("%lld\n" , Lucas(n + m , m)) ; 
	}
	return 0 ;
}

多重集的排列数 | 组合数

  • 多重集 : 指包含重复元素的集合, 即为 \(S = {n_1 \times a_1 , n_2 \times a_2 … n_k \times a_k}\)
    其中表示为含有 \(n_1\)\(a_1\)\(n_2\)\(a_2\) …… \(n_k\)\(a_k\) 的集合 。

\(S\) 的全排列个数为 $\displaystyle \frac{(\sum_{i=1}{k}n)!}{\prod_{k} n_i} $ .

说明:
主要想法就是去掉重复的元素,\(sum = \sum_{i = 1}^{k} n_i\) 中有 \(k\) 种不一样的球。 这 \(sum\) 个球的全排列就是多重集的排列数。

在这 \(sum\) 个元素中取出 \(m\) 个元素形成一个多重集(无序),产生的不同的多重集的组合数量为

\[\displaystyle C_{k + m - 1}^{k - 1} \]

证明:
咕了,是我菜了,没证明出来

不相邻的排列

\(1\text{~}n\) 的自然数内,取出 \(m\) 个数,这 \(m\) 个数都不相邻的组合有 \(C_{n - m + 1}^{m}\)

错位排列

根据 \(OI-WIKI\) 提供的思路,我们将其转化为实质化的题目: 现在有 \(n\) 个邮箱和 \(n\) 个信件,求解信件的编号不能和邮箱一样的组合数。

现在考虑放第 \(n\) 个信件

  • \(n - 1\) 个信件全部都被装错了
  • \(n - 1\) 个信件有一个没装错,其余全部装错了。

对于前面已经全部装错的,我们将当前的 \(n\) 这个信件随便插入到 \(n - 1\) 的一个范围就行,因为前面 \(n - 1\) 的信件不可能属于第 \(n\) 个邮箱,所以就有 \(n - 1\) 种情况。

对于第二种情况 : 这就像是走楼梯的递推式一样,可以一次性走一步, 其他的步数都是可以拼凑出来的,就像是 \(n - 1\) 的前面一样可能也是有一个信件的,综上,就是爬楼梯的递推式, \(f_{i} = f_{i -1} + f_{i - 2}\)

所以, 综上 , \(f_i = (i - 1)\times (f_{i - 1} + f_{i - 2})\)

圆排列

暂且咕了

posted @ 2021-04-11 21:54  SkyFairy  阅读(156)  评论(2编辑  收藏  举报