组合数学基本

版权声明:欢迎转载,请注出处 https://blog.csdn.net/qq_33850304/article/details/83090509

基础公式

先复习一下高中学的基础知识
A(n, m) = n!/(n-m)! 从n个元素中有顺序的取出m个元素。
C(n, m) = n!/((n-m)!m!) 从n个元素中无顺序的取出m个元素。
高考选择题中小技巧有排除法,分类讨论法,插空法等。
二项式定理:
在这里插入图片描述
帕斯卡恒等式:
C(n + 1,r)=C(n + 1,r) = C(n,r - 1) + C(n,r);
因为(n + 1,r)是在n + 1个元素中选择r个元素的无重复组合,那么对于集合中的某个元素,要么选,要么不选,如果选的话就是C(n,r - 1),如果不选就是C(n,r)。
它的用处主要是降低运算量,防止大数超时。

醒脑小例题

有n个人围着桌子吃饭,请问有多少种排列方法?要是其中两个人不想坐在一起呢?
解:(1)有n!/n种排列方法,因为圆桌需要指定一个开始坐的起点,n!则是没有考虑起点问题,需要/n,或者认为随机指定一个人先坐下,作为起点,剩下人再顺一定方向坐。
(2)设a,b不想坐在一起,那考虑ab坐在一起的情况,相当于n-1个人在围着桌子吃饭,其中a,b两人可以ab或ba就坐,所以坐在一起的情况有2*(n-2)!,故此小问答案为(n-1)!-2*(n-2)!

递推公式求通项

常见的有累加法,累乘法,构造法(设定参数法,拆分法,方程法构造出等差等比数列)
见此百度经验,跟高中题目差不多

常见运算实现

  • 求组合数C(n, r)
    公式看上去是比较简单,但考虑到数据量大时可能会造成超时,需要用帕斯卡恒等式降低复杂度。但是层数过多递归也会造成大量重复计算,直接打个二维表更好。
int C[1010][1010] = {0};
void pre() {
    for (int i = 0; i < 1010; ++i) {
        C[i][0] = 1;
        for (int j = 1; j <= i; ++j) {
            C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
        }
    }
}

还看到一个比较trick的计算方式,仔细研究了求组合数的实际运算元素,分子上是大于a(max(r, n-r))的数,分母上是小于等于b(min(r, n-r))的数。

long long int C1(int n, int r){
	int a, b;
	long long int res=1;
	a = r>(n-r)?r:n-r;
	b = n - a;
	for (int i=n; i>0; i--){
		if(i > a) res *= i;
		else if (i <= b) res /= i;
	}
	return res;
}
  • 快速幂求模
    排列组合中常见ab中a和b都是大数,一般需要的就是一边求幂,一边取余。
    基本操作:
//p^q mod m
for (int i=0; i<q; i++) res = (res * p)%m;

快速幂求模中要想办法降低a和b的规模,根据引理:积的取余等于取余的积的取余。首先我们可以a%=m 来降低a的大小,再通过合并a*a降低b的大小。代码实现:

long long Mode(long long a, long long b, long long mode)
{
	long long sum = 1;//最终结果
	while (b) {
		if (b & 1) {//位运算判断奇偶性
			sum = (sum * a) % mode;//是奇数的话先乘以下a
		}
		b /= 2;//降低了b的大小
		a = a * a % mode;//合并a*a,再mod一下
	}
	return sum;
}

猜你喜欢

转载自blog.csdn.net/qq_33850304/article/details/83090509