解题思路:
题目可以转换成 x1+x2+……+xn=m 有多少组解,m在题中可以取0~m。
利用插板法可以得出x1+x2+……+xn=m解的个数为C(n+m-1,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)。
现在就是要求C(n+m,m)%p。
因为n,m很大,这里可以直接套用Lucas定理的模板即可。
Lucas(n,m,p)=C(n%p,m%p,p)*Lucas(n/p,m/p,p); ///这里可以采用对n分段递归求解,
Lucas(x,0,p)=1;
将n,m分解变小之后问题又转换成了求(a/b)%p。
(a/b)%p可以转换成a*Inv(b,p) Inv(b,p)为b对p的逆元。
关于这个方程,引用一下论文
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
LL n, m, p;
LL Ext_gcd(LL a, LL b, LL &x, LL &y)
{
if (b == 0)
{
x = 1, y = 0;
return a;
}
LL ret = Ext_gcd(b, a % b, y, x);
y -= a / b * x;
return ret;
}
LL Inv(LL a, int m) ///求逆元a相对于m
{
LL d, x, y, t = (LL)m;
d = Ext_gcd(a, t, x, y);
if (d == 1) return (x % t + t) % t;
return -1;
}
LL Cm(LL n, LL m, LL p) ///组合数学
{
LL a = 1, b = 1;
if (m > n) return 0;
while (m)
{
a = (a * n) % p;
b = (b * m) % p;
m--;
n--;
}
return (LL)a * Inv(b, p) % p; ///(a/b)%p 等价于 a*(b,p)的逆元
}
// Lucas(n,m,p)=C(n%p,m%p,p)*Lucas(n/p,m/p,p); ///这里可以采用对n分段递归求解,
// Lucas(x,0,p)=1;
int Lucas(LL n, LL m, LL p) ///把n分段递归求解相乘
{
if (m == 0) return 1;
return (LL)Cm(n % p, m % p, p) * (LL)Lucas(n / p, m / p, p) % p;
}
int main()
{
// freopen("C:\\Users\\Sky\\Desktop\\1.in","r",stdin);
int T;
cin >> T;
while (T--)
{
scanf("%lld%lld%lld", &n, &m, &p);
printf("%d\n", Lucas(n + m, m, p));
}
return 0;
}