hdu 3037 Saving Beans(lucas定理模板)

题意:

求在n棵树上摘不超过m颗豆子的方案,结果对p取模。

解析:

题目可以转换成 x1+x2++xn=m 有多少组解,m在题中可以取0~m。

利用插板法可以得出 x1+x2++xn=m
解的个数为 Cn1n+m1=Cmn+m1

则题目解的个数可以转换成求 sum=C0n+m1+C1n+m1+C2n+m1+Cmn+m1

利用公式 Crn=Crn1+Cr1n1==>sum=Cmn+m

那么答案,就是要求 Cmn+m%p

因为n,m很大,这里可以直接套用Lucas定理的模板即可。

关于Lucas定理

Lucas(n,m,p)=C(n%p,m%p,p)Lucas(n/p,m/p,p)
//这里可以采用对n分段递归求解

Lucas(x,0,p)=1

nm 分解变小之后问题又转换成了求 (a/b)%p

(a/b)%p 可以转换成 aInv(bp)
Inv(b,p) 为b对p的逆元。

my code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;  
typedef long long ll;
const int N = 100005;
ll fac[N];

ll modpow(ll a, ll b, ll MOD) {
    ll ret = 1;
    while(b) {  
        if(b&1) ret = (ret*a)%MOD;
        a = (a*a)%MOD;
        b>>=1;
    }
    return ret;
}


ll getFactor(ll p) {
    fac[0] = 1;
    for(int i = 1; i <= p; i++)
        fac[i] = (fac[i-1]*i) % p;
}

ll Lucas(ll n, ll m, ll p) {  
    ll ret=1;
    while(n && m) {
        ll a = n%p, b = m%p;  
        if(a<b) return 0; 
        ret = (ret * fac[a] * modpow(fac[b]*fac[a-b]%p, p-2, p)) % p;
        n/=p;
        m/=p;
    }
    return ret;  
}  

int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        ll n, m, p;
        scanf("%I64d%I64d%I64d", &n, &m, &p);
        getFactor(p);
        printf("%I64d\n",Lucas(n+m,m,p));
    }
    return 0;
}

你可能感兴趣的:(HDU,3037)