组合数(三种求法)

1.递推法(杨辉三角)

从 n个数不同的数中选出 m个的方案数是 C_{n}^{m},对第一个数有选和不选两种决策,若不选,则从剩下的 n-1个中选 m个,即C_{n-1}^{m};若选,则从剩下的 n-1个中选 m-1个,即C_{n-1}^{m-1}

所以递推式 C_{n}^{m}=C_{n-1}^{m}+C_{n-1}^{m-1}

根据公式我们发现和杨辉三角一样,C_{n}^{m}当n==1,m=0时,为1;当n==3,m==2时,为3。m,n数据小的时候我们可以用递推,但如果数据范围太大就要用其他方法

void getc(){
	for(int i=0;i<n;i++){
		for(int j=0;j<=i;j++){
			if(j==0)
			a[i][j]=1; //数组a存储的是n=i,m=j的组合数
			else{
				a[i][j]=(a[i-1][j]+a[i-1][j-1])%mod;
			}
		}
	}
}

2.快速幂+乘法逆元

因为组合数太大,算法竞赛中常见问题是求解组合数取模C_{n}^{m}%p(其中p为质数),C_{n}^{m}=\frac{n!}{(n-m)!}

在取模的条件下,除以一个数等价于乘以这个数的乘法逆元,例如,x /2%107=x*54%107,那么54就是 2在模107条件下的乘法逆元

1.求阶乘,求出n!,m!,(n-m)!  %p,for循环递推即可,每次乘法后取模

2.求出m!,(n-m)! 的乘法逆元(%p),因为p为质数,所以 x的逆元即 x^{p-2},用快速幂即可求出

易得,C_{n}^{m}=a[n] * (a[n]的逆元)*(a[n-m]的逆元),总时间复杂度为O(n)

ll qpow(ll y,int z,int p){
	ll ans=1;
    while(y){
    	if(y&1)
    	ans*=y%p;
    	y=y*y%p;
    	y>>=1;
	}
    return ans;
}
long long c(long long n,long long m){
	if(m>n)
	return 0;
	return ((a[n]*qpow(a[m],p-2,p))%p*qpow(a[n-m],p-2,p)%p);
    //a[x]为递推求得的x的阶乘
}

3.卢卡斯定理

前半部分可以继续使用卢卡斯定理递归求解,后半部分可以用第二种方法直接求解,后半部分的m,n都会小于p,时间复杂度为O(n)。适用于n较大,p较小或n较小的情况

long long lucas(long long n,long long m){
	if(!m)  //也就是m>0
	return 1;
	return lucas(n/p,m/p)*c(n%p,m%p)%p; //前半部分为递归,后半部分 c为方法二中的函数
}

相关推荐

  1. 【算法模板】数论:杨辉三角组合

    2024-07-22 19:06:03       20 阅读
  2. 东方博宜1957 - 的平均数

    2024-07-22 19:06:03       20 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-22 19:06:03       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-22 19:06:03       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-22 19:06:03       45 阅读
  4. Python语言-面向对象

    2024-07-22 19:06:03       55 阅读

热门阅读

  1. python之参数注解介绍

    2024-07-22 19:06:03       15 阅读
  2. 学习opencv

    2024-07-22 19:06:03       16 阅读
  3. DP学习——中介者模式

    2024-07-22 19:06:03       17 阅读
  4. 交换机(Switches)和桥(Bridges)的区别

    2024-07-22 19:06:03       15 阅读
  5. 测试面试宝典(二十一)—— get和post的区别

    2024-07-22 19:06:03       14 阅读
  6. ESP8266AT指令查看有哪些指令可用(3)

    2024-07-22 19:06:03       13 阅读