如果 a*x ≡ 1 (mod p),且gcd(a,p)=1 ,则称a关于模p的乘法逆元为x。
求法:扩展欧几里得
由 a*x ≡ 1 (mod p)
<==>(a*x - 1)%p == 0
<==>a*x - 1 == p*y
<==>a*x -p*y == 1
可得 <==>a*x+py == 1 (前提gcd(a,b) == 1)
正整数 x = ( (x % p) +p) %p
求得的正整数x 即为a关于模p的乘法逆元
应用:
对于(a+b)%p 和 (a-b)%p , 有
(a+b)%p=(a%p+b%p)%p
(a*b)%p=(a%p)*(b%p)%p
而(a/b)%p,没有相应的公式,数会变得很大
可以通过乘法逆元来求:
(a/b)%p = a * inv(b,p)%p
组合
给出组合数C(n,m), 表示从n个元素中选出m个元素的方案数。例如C(5,2) = 10, C(4,2) = 6.可是当n,m比较大的时候,C(n,m)很大!于是xiaobo希望你输出 C(n,m) mod p的值!
Input
输入数据第一行是一个正整数T,表示数据组数 (T <= 100) 接下来是T组数据,每组数据有3个正整数 n, m, p (1 <= m <= n <= 10^9, m <= 10^4, m < p < 10^9, p是素数)
Output
对于每组数据,输出一个正整数,表示C(n,m) mod p的结果。
Sample Input
2
5 2 3
5 2 61
Sample Output
1
10
组合数递推公式 c (n,m) = c(n,m-1) * (n-m+1) / m;
直接算数太大存不下
用乘法逆元 c(n,m) = c(n,m-1) * (n-m+1) * inv(m,p) % p ;
#include<stdio.h>
long long ex_gcd(long long a,long long b,long long &x, long long &y)
{
if(b==0)
{
long long g=a;
x=1;
y=0;
return g;
}
else
{
long long g =ex_gcd(b,a%b,x,y);
long long x1=y,y1=x-a/b*y;
x=x1,y=y1;
return g;
}
}
long long inv(long long m, long long p)
{
long long x, y;
ex_gcd(m,p,x,y);
return ((x%p)+p)%p;
}
long long c(long long n, long long m, long long p)
{
if(m > n-m)
m = n-m;
long long ans = 1;
for(long long i = 1; i <= m; i ++)
{
ans = ans*(n-i+1)%p*inv(i,p)%p;
}
return ans;
}
int main()
{
long long m,p,n,t;
scanf("%lld",&t);
while(t --)
{
scanf("%lld %lld %lld",&n, &m, &p);
printf("%lld\n",c(n,m,p));
}
return 0;
}