您的位置:首页 > 其它

FZU 2020 Comb(扩展欧几里德求逆元)

2013-05-13 18:50 246 查看
yimao哥的求组合数取余的模板,涨姿势了(¯﹃¯)

/*
组合数的计算;

C(n,m)= n*(n-1)*...*(n-m+1) / m!;
注意到本题,m最大为10^4,1s的时限(保守估计是10^7),所以直接计算不超时;

分子: fz: 表示分子:fz= n*(n-1)*...*(n-m+1);最多10^4次乘法,
边乘要边取模;

分母: fm: 表示分母:fm= m*(m-1)*...*2*1;最多10^4次乘法,
边乘要边去模;

最后分母要求对模数p的逆元再与分子相乘;
*/

#include <cstdio>
#include <iostream>
using namespace std;

int ext_gcd(int a, int b, int &x, int &y){
if( !b ) return x=1,y=0,a;
int d= ext_gcd(b, a%b, y, x);
y-= a/b*x;
return d;
}
int Inv(int a, int m){
int d,x,y;
d= ext_gcd(a,m,x,y);
return (x%m+m)%m;
}

/*计算组合数C(n,m)%p,其中m<=10^4,p是素数,且m<p;
fm: 表示分母:fm= m*(m-1)*...*2*1;
fz: 表示分子:fz= n*(n-1)*...*(n-m+1);

取余对除法不具有封闭性,
必须求出除数(即分母)对模数p的逆元,转换成乘法;
C(n,m)= n*(n-1)*...*(n-m+1) / m!;
C(n,m)%p = (fz%p) * Inv( (fm%p), p ) %p;

*/
long long Comb(int n, int m, int p){
long long fm=1, fz= 1;
for(int i=2; i<=m; ++i)
fm= fm*i%p;
for(int i=n-m+1; i<=n; ++i)
fz= fz*i%p;
long long ret= fz * Inv( fm, p ) %p;
return ret<0 ? ret+p : ret;
}

int main()
{
//freopen("E.in","r",stdin);
int T,n,m,p;
cin>>T;
while( T-- ){
cin>>n>>m>>p;
cout<< Comb( n, m, p ) <<endl;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: