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

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;
    }
}


你可能感兴趣的:(FZU 2020 Comb(扩展欧几里德求逆元))