总结组合数的几种求法(模板)

目录

    • way1.打表C(n,m)
    • way2. 阶乘无模
    • way3.乘法逆元+快速幂+阶乘
    • way4.Lucas定理


way1.打表C(n,m)

原理:

  • 杨辉三角
  • ∑ i = m n C i m = C n + 1 m + 1 \sum_{i=m}^{n}C_{i}^{m}=C_{n+1}^{m+1} i=mnCim=Cn+1m+1
  • 即下图中绿色方框的数等于红色方框内数的总和:
    总结组合数的几种求法(模板)_第1张图片

空间:

  • O(nm)

时间:

  • 预处理O(nm)
  • 查询O(1)
for(int i=0;i<=n;i++){
	c[i][0]=c[i][i]=1;
	for(int j=1;j<i;j++)
		c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}

way2. 阶乘无模

原理:

  • 组合数基本公式
  • C n m = n ! m ! ( n − m ) ! C_n^m=\frac{n!}{m!(n-m)!} Cnm=m!(nm)!n!

空间:

  • O(n)

时间:

  • 预处理O(1)
  • 查询O(n)

但这有个缺点就是涉及除法,无法直接取模

所以我们就要引入乘法逆元:


way3.乘法逆元+快速幂+阶乘

原理:

  • 费马小定理:
  • a p − 1 ≡ 1 ( m o d   p ) a^{p-1} \equiv 1 (mod\, p) ap11(modp) (p是素数)
  • 两边同除a得:
  • a p − 2 ≡ 1 a ( m o d   p ) a^{p-2} \equiv \frac{1}{a} (mod\, p) ap2a1(modp)
  • 我们知道mod p意义下 1 a \frac{1}{a} a1就是a的逆元
  • 因此可得mod p意义下a的逆元 i n v ( a ) = a p − 2 inv(a)=a^{p-2} inv(a)=ap2
  • 而这个 a p − 2 a^{p-2} ap2又可以用快速幂一只log求得

空间:

  • O(n)

时间:

  • 预处理O(n)
  • 查询O(log p)
int pow(int x,int y){ //快速幂
	int res=1;
	x%=p;
	for(;b;b>>=1,a=a*a%p) if(b&1) res=res*a%p;
    return res;
}
int inv(int x,int p){ //求逆元
	return pow(x,p-2);
}
fac[0]=1;
for(int i=1;i<=n;i++)
	fac[i]=fac[i-1]*i; //预处理出阶乘
return ((fac[n]*inv(fac[m],p))%p*inv(fac[n-m],p))%p;

way4.Lucas定理

Lucas定理是用于处理组合数取模的定理

通常用于解决阶乘无法解决的问题。

原理:

  • L u c a s ( n , m , p )   m o d   p = L u c a s ( n p , m p , p ) ∗ C n   m o d   p m   m o d   p   m o d   p Lucas(n,m,p)\: mod\: p=Lucas(\frac{n}{p},\frac{m}{p},p)*C_{n\: mod\: p}^{m\: mod\: p}\: mod\: p Lucas(n,m,p)modp=Lucas(pn,pm,p)Cnmodpmmodpmodp
  • L u c a s ( n , m , p )   m o d   p = C n m   m o d   p Lucas(n,m,p)\: mod\: p=C_n^m\: mod \: p Lucas(n,m,p)modp=Cnmmodp

以洛谷P3807为例

#define int long long
int t,n,m,p,f[100005];
int pow(int x,int y,int p){ //快速幂
	x%=p;
	int ans=1;
	for(int i=y;i;i>>=1,x=x*x%p) if(i&1) ans=ans*x%p;
	return ans;
}
int C(int n,int m,int p){ //求组合数
	if(m>n) return 0;
	return ((f[n]*pow(f[m],p-2,p))%p*pow(f[n-m],p-2,p)%p);
}
int lucas(int n,int m,int p){ //Lucas定理
	if(!m) return 1;
	return C(n%p,m%p,p)*lucas(n/p,m/p,p)%p;
}
signed main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d%d",&n,&m,&p);
		f[0]=1;
		for(int i=1;i<=p;i++)
			f[i]=(f[i-1]*i)%p;
		printf("%lld\n",lucas(n+m,m,p));
	}
}

你可能感兴趣的:(模板,数论)