组合数取模
求模意义下的组合数\(C^k_n\;mod\;p\)是十分常见的问题,根据数据的不同也有多种求法,下面来谈谈一些常见的解法。
\([0]: 求n!\;mod\;p\)
代码:
void init()
{
fact[0]=1;
rep(i,1,p) fact[i]=(fact[i-1]*i)%p;
}
int fact_mod(int n,int p,int &e) //return a, n=a*p^e
{
e=0;
if (n<=1) return 1;
int temp=fact_mod(n/p,p,e);
e+=n/p;
if ((n/p)&1) return temp*((p-1)*fact[n%p])%p; else return temp*(fact[n%p])%p;
}
\([1]: n,k\leq1000,p无限制\)
因为数据小,直接使用杨辉三角求解,复杂度为\(O(n^2)+O(1)\)
\([2]: n,k\leq 10^5,p为质数\)
因为p为质数,我们预处理出\([1,100000]\)的阶乘及其逆元,计算时直接用预处理的结果计算,复杂度为\(O(nlogn)+O(1)\)
\([3]: n,k\leq10^{18},p为质数且p\leq10^5\)
根据lucas定理,将n,k转化为p进制:$$n=\prod_{i=0}^m n_ip^i| n_i<p$$
\[k=\prod_{i=0}^m k_ip^i| k_i<p
\]
则有:
\[C_n^k=\prod_{i=0}^k C_{n_i}^{k_i} \; mod\;p
\]
转化为情况2,复杂度为\(O(nlogn)+O(log_pn)\)
代码:
void exgcd(int a,int b,int &x,int &y) {if (b==0) x=1,y=0; else exgcd(b,a%b,y,x),y-=(a/b)*x;}
int C(int x,int y)
{
if (x<y) return 0;
int inv;
exgcd((frac[y]*frac[x-y])%p,p,inv,k); //y*k mod p = 1
inv=(inv+p)%p;
return (frac[x]*inv)%p;
}
int lucas(int n,int m,int p)
{
if ((n<p)&&(m<p)) return C(n,m);
return (C(n%p,m%p)*lucas(n/p,m/p,p))%p;
}
\([4]: n,k\leq10^5,p\leq10^5,p为任意数\)
这种情况最麻烦,我们首先筛出[1,n]的质数表,对于每个质数求一下n,m,n-m的因子之差,最后快速幂乘就可以了,复杂度为\(O(n)+O(n)\)

浙公网安备 33010602011771号