容易推得答案就是C(2n,n)-C(2n,n+k),然而数据不保证p为质数,这样一来就不能直接逆元求组合数了。
我们可以用线性筛预处理出每个数的最小质因子,对于每个数将其分解成质数乘积的形式,最后统计每个质数的贡献。
此题并不难,值得记录一下的原因是这题质因数分解相消的方法比较巧妙,仅需记录最小质因数即可。
代码如下
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
int prime[maxn],mp[maxn];
bool v[maxn];
int tot;
int num[maxn];
long long mod;
void euler()
{
for(int i=2;i<maxn;i++)
{
if(!v[i])
prime[++tot]=i,mp[i]=tot;
for(int j=1;j<=tot&&(i*prime[j]<maxn);j++)
{
v[i*prime[j]]=1;
mp[i*prime[j]]=j;
if(i%prime[j]==0)
break;
}
}
}
long long mpow(long long a,long long b)
{
long long res=1,base=a;
while(b)
{
if(b&1)
res=res*base%mod;
base=base*base%mod;
b>>=1;
}
return res;
}
void func(int x,int op)
{
while(x!=1)
num[mp[x]]+=op,x/=prime[mp[x]];
}
long long getans()
{
long long res=1;
for(int i=1;i<=tot;i++)
{
if(num[i])
res=(res*mpow(prime[i],num[i]))%mod;
}
return res;
}
int main()
{
int n,k;
cin>>n>>k>>mod;
euler();
for(int i=n+1;i<=2*n;i++)
func(i,1);
for(int i=1;i<=n;i++)
func(i,-1);
long long ans=getans();
memset(num,0,sizeof num);
for(int i=n+k+1;i<=2*n;i++)
func(i,1);
for(int i=1;i<=n-k;i++)
func(i,-1);
ans=(ans-getans()+mod)%mod;
cout<<ans<<endl;
return 0;
}