组合数取模

求模意义下的组合数\(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)\)

posted @ 2017-04-18 11:06  Krew  阅读(736)  评论(0)    收藏  举报