HDU3037 Saving Beans(Lucas定理+乘法逆元)

题目大概问小于等于m个的物品放到n个地方有几种方法。

即解这个n元一次方程的非负整数解的个数$x_1+x_2+x_3+\dots+x_n=y$,其中0<=y<=m。

这个方程的非负整数解个数是个经典问题,可以+1转化正整数解的个数用插板法解决:$C_{y+n-1}^{n-1}=C_{y+n-1}^y$。

而0<=y<=m,最后的结果就是——

$$\sum_{i=0}^m C_{i+n-1}^i$$

$$C_{n-1}^0+C_{n}^1+C_{n+1}^2+\dots+C_{n-1+m}^m$$

$$C_{n}^0+C_{n}^1+C_{n+1}^2+\dots+C_{n-1+m}^m$$

$$C_{n+1}^1+C_{n+1}^2+\dots+C_{n-1+m}^m\ \tag{$C_{n+1}^1=C_{n}^0+C_{n}^1$}$$

$$C_{n+2}^2+\dots+C_{n-1+m}^m\ \tag{$C_{n+2}^2=C_{n+1}^1+C_{n+1}^2$}$$

$$\vdots$$

$$C_{n+m}^m$$

于是就推算出结果是$C_{n+m}^m$。那么就是计算$C_{n+m}^m\ mod \ p$,其中1<=n,m<=1000000000,1<p<100000且p为质数。

这时就是用Lucas定理来计算这种大组合数的模:$Lucas(n,m)\equiv C_{n\%p}^{m\%p}\times Lucas(n/p,m/p)\pmod p$。

另外计算组合数时,利用模p下的乘法逆元,$C_n^m\equiv\frac {n!}{(n-m)!m!}\equiv n!\times((n-m)!m!)^{-1} \pmod p$

而计算逆元没必要用扩展欧几里得算法,因为p是质数,利用费马小定理可以推出n在模p下的乘法逆元为$n^{p-2}\ mod\ p$。

 1 #include<cstdio>
 2 #include<cstring>
 3 using namespace std;
 4 long long ine(long long n,long long p){
 5     long long res=1,m=p-2;
 6     while(m){
 7         if(m&1) res=res*n%p;
 8         n=n*n%p;
 9         m>>=1;
10     }
11     return res;
12 }
13 long long fact[100000]={1};
14 long long lucas(long long n,long long m,long long p){  
15     long long res=1;  
16     while(n&&m){  
17         long long a=n%p,b=m%p;  
18         if(a<b) return 0;  
19         res=res*fact[a]*ine(fact[b]*fact[a-b]%p,p)%p;  
20         n/=p; m/=p;
21     }
22     return res;
23 }
24 int main(){
25     long long n,m,p;
26     int t;
27     scanf("%d",&t);
28     while(t--){
29         scanf("%lld%lld%lld",&n,&m,&p);
30         for(int i=1; i<p; ++i) fact[i]=fact[i-1]*i%p;
31         printf("%lld\n",lucas(n+m,m,p));
32     }
33     return 0;
34 } 

 

你可能感兴趣的:(HDU3037 Saving Beans(Lucas定理+乘法逆元))