2 1 2 5 2 1 5
3 3HintHint 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.
题解:组合数取模(lucas定理)
题目大意:从n棵不同的树上取不超过M颗豆子(豆子无差异),有多少种取法。
题目可以转换成 x1+x2+……+xn=m 有多少组解,m在题中可以取0~m。
对于 x1+x2+……+xn=m,需要利用到插板法。
插板法:插板法就是在n个元素间的(n-1)个空中插入 若干个(b)个板,可以把n个元素分成(b+1)组的方法。
应用插板法必须满足三个条件:
(1) 这n个元素必须互不相异
(2) 所分成的每一组至少分得一个元素
(3) 分成的组别彼此相异
那么这相当于是m 个元素,插入n个板子,但是所分成的每组元素可以剩余,条件(2)不满足,此时如果在3个箱子种各预先插入1个元素,则问题就等价于把n+m个元素插入n个板子,此时的答案就是C(n+m-1,n-1) =C(n+m-1,m) (C(N,M)=C(N,N-M)) 。
则题目解的个数可以转换成求 sum=C(n+m-1,0)+C(n+m-1,1)+C(n+m-1,2)……+C(n+m-1,m)利用公式C(n,r)=C(n-1,r)+C(n-1,r-1) == > sum=C(n+m,m)。
sum=C(n+m-1,0)(m=0)+C(n+m-1,1)(m=1)+C(n+m-1,2)(m=2)……+C(n+m-1,m)(m=m)
=C(n-1,0)+C(n,1)+C(n+1,2)....+C(n-m-1,m)
因为C(n-1,0)=C(n,0)=1
所以式子可以利用C(n,r)=C(n-1,r)+C(n-1,r-1) ,进行化简最终得到C(n+m,m)
现在就是要求C(n+m,m)%p。
lucas定理:
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)
但是lucas定理只有在A,B<=10^18,P为质数,p<=10^5时才可以使用(如果p<=10^9,但是n,m较小的话,貌似也可以用lucas定理,但是没法预处理阶乘,只能单独计算组合数,总之具体情况具体分析)
这道题的话可以先预处理出阶乘,根据公式c(n,m)=n!/(m!(n-m)!),因为是模意义下所以可以转化为n!*inv(m!(n-m)!) mod p,因为p是质数,根据费马小定理,可得 inv(m!(n-m)!)=(m!(n-m)!)^p-2,可用快速幂求解。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define ll long long using namespace std; ll k[500003],n,m,p; int t; void calc(ll p) { k[0]=1; for (ll i=1;i<=p;i++) k[i]=(ll)k[i-1]*i%p; } ll quick(ll num,ll x) { ll ans=1; ll base=(ll)num%p; while(x) { if (x&1) ans=ans*base%p; x>>=1; base=base*base%p; } return ans%p; } ll c(ll x,ll y) { if (y>x) return 0; return (ll)k[x]*quick(k[y]*k[x-y],p-2)%p; } ll lucas(ll n,ll m,ll p) { if (m==0) return 1; return (ll)c(n%p,m%p)*lucas(n/p,m/p,p)%p; } int main() { freopen("a.in","r",stdin); freopen("my.out","w",stdout); scanf("%d",&t); for (int i=1;i<=t;i++) { scanf("%I64d%I64d%I64d",&n,&m,&p); calc(p); printf("%I64d\n",lucas(n+m,m,p)%p); } }