传送门
中文题目背景:
虽然冬天很远,松鼠不得不日夜工作以拯救豆子。他们需要充足的食物来度过那些漫长的寒冷日子。过了一段时间,松鼠家庭认为他们必须解决一个问题。他们认为他们会在不同的树上拯救豆子。然而,由于现在的食物不够,他们只吃不到豆子。他们想知道有多少种方法在N树中保存不超过M bean(它们是相同的)。
现在他们求助于你,你应该给他们答案。结果可能非常巨大,你应该输出模P,因为松鼠无法识别大数。
题目大意:
求在n棵树上摘不超过m颗豆子的方案,结果对p取模。
题解:
根据题意可以明白最终所求答案为,如果直接枚举i的话很显然会TLE。于是我们可以用一个很神奇的结论(组合恒等式)把答案化为,又因为模数p为质数,直接上普通lucas定理解决。
AC代码:
#include
#include
#define ll long long
using namespace std;
ll a[200005],p,t,n,m;
ll exgcd(ll a,ll b,ll &x,ll &y){//扩展欧几里得
if(b){
ll d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
x=1;y=0;return a;
}
ll inv(ll x,ll mod){//求x在模mod下的逆元
ll anx,any;exgcd(x,mod,anx,any);return (anx+mod)%mod;
}
ll C(ll n,ll m){//求C(n,m)%p n<=10^5 m<=10^5
if(m>n)return 0;
return a[n]*inv(a[m],p)%p*inv(a[n-m],p)%p;
}
ll lucas(ll n,ll m){//lucas定理求 C(n,m)%p n,m可以很大
if(!m)return 1;
return C(n%p,m%p)*lucas(n/p,m/p)%p;
}
void prep(){//预处理n!%p
a[0]=1;
for(ll i=1;i<=p;i++){
a[i]=i*a[i-1]%p;
}
}
void work(){
scanf("%lld%lld%lld",&n,&m,&p);
prep();
printf("%lld\n",lucas(n+m,n));
}
int main(){
scanf("%lld",&t);
while(t--){
work();
}
return 0;
}