天天看點

[ACM] hdu 3037 Saving Beans (Lucas定理,組合數取模) Saving Beans

Saving Beans

Problem Description Although winter is far away, squirrels have to work day and night to save beans. They need plenty of food to get through those long cold days. After some time the squirrel family thinks that they have to solve a problem. They suppose that they will save beans in n different trees. However, since the food is not sufficient nowadays, they will get no more than m beans. They want to know that how many ways there are to save no more than m beans (they are the same) in n trees.

Now they turn to you for help, you should give them the answer. The result may be extremely huge; you should output the result modulo p, because squirrels can’t recognize large numbers.  

Input The first line contains one integer T, means the number of cases.

Then followed T lines, each line contains three integers n, m, p, means that squirrels will save no more than m same beans in n different trees, 1 <= n, m <= 1000000000, 1 < p < 100000 and p is guaranteed to be a prime.  

Output You should output the answer modulo p.  

Sample Input

2
1 2 5
2 1 5
        

Sample Output

3
3

   
    
     Hint
    
Hint

For sample 1, squirrels will put no more than 2 beans in one tree. Since trees are different, we can label them as 1, 2 … and so on. 
The 3 ways are: put no beans, put 1 bean in tree 1 and put 2 beans in tree 1. For sample 2, the 3 ways are:
 put no beans, put 1 bean in tree 1 and put 1 bean in tree 2.

   
    
        

Source 2009 Multi-University Training Contest 13 - Host by HIT 題意為:将不超過m個豆子放在n棵不同的樹上,一共有多少種方法。

以下轉載于:

http://blog.csdn.net/tju_virus/article/details/7843248

題目相當于求n個數的和不超過m的方案數。

如果和恰好等于m,那麼就等價于方程x1+x2+...+xn = m的解的個數,利用插闆法可以得到方案數為:

(m+1)*(m+2)...(m+n-1)  = C(m+n-1,n-1) = C(m+n-1,m)

現在就需要求不大于m的,相當于對i = 0,1...,m對C(n+i-1,i)求和,根據公式C(n,k) = C(n-1,k)+C(n-1,k-1)得

C(n-1,0)+C(n,1)+...+C(n+m-1,m)

= C(n,0)+C(n,1)+C(n+1,2)+...+C(n+m-1,m)

= C(n+m,m)

現在就是要求C(n+m,m) % p,其中p是素數。

然後利用Lucas定理的模闆就可以輕松的求得C(n+m,m) % p的值

下面簡單介紹一下Lucas定理:

Lucas定理是用來求 C(n,m) mod p的值,p是素數(從n取m組合,模上p)。

描述為:

Lucas(n,m,p)=C(n%p,m%p)* Lucas(n/p,m/p,p)

Lucas(x,0,p)=1;

A、B是非負整數,p是質數。AB寫成p進制:A=a[n]a[n-1]...a[0],B=b[n]b[n-1]...b[0]。

則組合數C(A,B)與C(a[n],b[n])*C(a[n-1],b[n-1])*...*C(a[0],b[0])  modp同餘

即:Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p) 

簡單的了解就是:

以求解n! % p 為例,把n分段,每p個一段,每一段求得結果是一樣的。但是需要單獨處理每一段的末尾p,2p,...,把p提取出來,會發現剩下的數正好又是(n/p)! ,相當于

劃歸了一個子問題,這樣遞歸求解即可。

這個是單獨處理n!的情況,當然C(n,m)就是n!/(m! *(n-m)!),每一個階乘都用上面的方法處理的話,就是Lucas定理了

Lucas最大的資料處理能力是p在10^5左右。

而C(a,b) =a! / ( b! * (a-b)! ) mod p

其實就是求 ( a! / (a-b)!)  * ( b! )^(p-2) mod p

  (上面這一步變換是根據費馬小定理:假如p是質數,且a,p互質,那麼a的(p-1)次方除以p的餘數恒為1,

那麼a和a^(p-2)互為乘法逆元,則(b / a) = (b * a^(p-2) ) mod p)

代碼:

#include <iostream>
#include <string.h>
#include <cmath>
#define ll long long
using namespace std;
const int maxn=100002;
ll n,m,p;
ll fac[maxn];

void getfac(ll p)//預處理階層
{
    fac[0]=1;
    for(int i=1;i<=p;i++)
        fac[i]=fac[i-1]*i%p;
}

ll power(ll a,ll n,ll p)//快速幂運算
{
    ll ans=1;
    while(n)
    {
        if(n&1)
            ans=ans*a%p;
        a=a*a%p;
        n/=2;
    }
    return ans;
}

ll lucas(ll n,ll m,ll p)
{
    ll ans=1;
    while(n&&m)
    {
        ll a=n%p;
        ll b=m%p;
        if(a<b) return 0;
        ans=(ans*fac[a]*power(fac[b]*fac[a-b]%p,p-2,p))%p;//  fac[b]*fac[a-b]後面别忘了%p,否則WA
        n/=p;
        m/=p;
    }
    return ans;
}


int main()
{
    int t;cin>>t;
    while(t--)
    {
        cin>>n>>m>>p;
        getfac(p);
        cout<<lucas(n+m,m,p)<<endl;
    }
    return 0;
}
           

Lucas 定理兩種寫法:

ll lucas(ll n,ll m,ll p)
{
    ll ans=1;
    while(n&&m)
    {
        ll a=n%p;
        ll b=m%p;
        if(a<b) return 0;
        ans=(ans*fac[a]*power(fac[b]*fac[a-b]%p,p-2,p))%p;//  fac[b]*fac[a-b]後面别忘了%p,否則WA
        n/=p;
        m/=p;
    }
    return ans;
}
           
int Lucas(lld n,lld m,lld p)  
{  
    if(m==0)  
        return 1;  
    return((lld)Cm(n%p,m%p,p)*(lld)Lucas(n/p,m/p,p))%p;  
} 
           
int Cm(lld n,lld m,lld p)  
{  
    lld a = 1,b = 1;  
    if(m > n) return 0;  
    //實作(a!/(a-b)!) * (b!)^(p-2)) mod p,由于n比較大,是以,此處不知道有什麼好的優化  
    while(m)  
    {  
        a = (a * n) % p;  
        b = (b * m) % p;  
        m--;  
        n--;  
    }  
    return ((lld)a * (lld)Pow(b,p-2,p))%p;  
}
           

繼續閱讀