上来先感谢大佬的文章:https://blog.csdn.net/clove_unique/article/details/54571216
题目链接:http://codeforces.com/gym/100633/problem/J
题意:
求
Ckn%p C n k % p的值,其中 1≤n≤1018 1 ≤ n ≤ 10 18 , 0≤k≤n 0 ≤ k ≤ n , 2≤p≤1000000 2 ≤ p ≤ 1000000 .
分析:
若p为质数,可以用lucas定理直接得,但题目中p不一定为质数,则需要用拓展lucas定理+中国剩余定理。
结论:
拓展lucas定理:
可求方程:
中国剩余定理(又名孙子定理)
可求
的最小正整数解,解为
题解:
对于p,将它表示成
唯一的问题在于在求
代码实现:
long long x=0,ans;
for(long long i=n;i;i/=pi) x+=i/pi;
for(long long i=k;i;i/=pi) x-=i/pi;
for(long long i=n-k;i;i/=pi) x-=i/pi;
ans=a*mod_inverser(b,pk)%pk*mod_inverser(c,pk)*pow_mod(pi,x,pk)%pk;
解释一下
ipi
i
p
i
的含义:
以求
n!
n
!
为例:假设
n=100,pi=2
n
=
100
,
p
i
=
2
100!=100∗99∗98∗...∗1
100
!
=
100
∗
99
∗
98
∗
.
.
.
∗
1
=(99∗97∗95∗...∗1)∗250∗(1∗2∗3∗...∗50)
=
(
99
∗
97
∗
95
∗
.
.
.
∗
1
)
∗
2
50
∗
(
1
∗
2
∗
3
∗
.
.
.
∗
50
)
=(99∗97∗95∗...∗1)∗250∗(1∗3∗...∗49)∗225∗(1∗2∗...∗25)
=
(
99
∗
97
∗
95
∗
.
.
.
∗
1
)
∗
2
50
∗
(
1
∗
3
∗
.
.
.
∗
49
)
∗
2
25
∗
(
1
∗
2
∗
.
.
.
∗
25
)
……
所以,一直除
pi
p
i
可将
n!
n
!
表示为
代码:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
long long exgcd(long long a,long long b,long long &x,long long &y)
{
long long d=a;
if(b!=0)
{
d=exgcd(b,a%b,y,x);
y-=(a/b)*x;
}else{
x=1;
y=0;
}
return d;
}
long long pow_mod(long long a,long long b,long long p)
{
long long res=1;
while(b)
{
if(b&1) res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
long long mod_inverser(long long a,long long m)
{
long long x,y;
exgcd(a,m,x,y);
return (m+x%m)%m;
}
long long cal(long long n,long long pi,long long pk)
{
if(n==0) return 1;
long long ans=1;
if(n/pk)
{
for(long long i=2;i<=pk;i++)
{
if(i%pi)
ans=ans*i%pk;
}
ans=pow_mod(ans,n/pk,pk);
}
for(long long i=2;i<=n%pk;i++)
{
if(i%pi)
ans=ans*i%pk;
}
return ans*cal(n/pi,pi,pk)%pk;
}
long long C(long long n,long long k,long long p,long long pi,long long pk)
{
if(k>n) return 0;
long long a=cal(n,pi,pk),b=cal(k,pi,pk),c=cal(n-k,pi,pk);
long long x=0,ans;
for(long long i=n;i;i/=pi) x+=i/pi;
for(long long i=k;i;i/=pi) x-=i/pi;
for(long long i=n-k;i;i/=pi) x-=i/pi;
ans=a*mod_inverser(b,pk)%pk*mod_inverser(c,pk)*pow_mod(pi,x,pk)%pk;
return ans*(p/pk)%p*mod_inverser(p/pk,pk)%p;
}
int main()
{
long long n,k,p;
cin>>n>>k>>p;
long long ans=0;
for(long long x=p,i=2;i<=p;i++)
{
if(x%i==0)
{
long long pk=1;
while(x%i==0)
{
pk*=i;
x/=i;
}
ans=(ans+C(n,k,p,i,pk))%p;
}
}
cout<<ans<<endl;
return 0;
}