链接:https://ac.nowcoder.com/acm/contest/317/H
来源:牛客网
题目描述
小a是一个健忘的人,由于他经常忘记做作业,因此老师对他很恼火。
小a马上就要开学了,他学期一共2n2n天,对于第ii天,他有可能写了作业,也可能没写作业,不过他自己心里还有点B数,因此他会写恰好nn天的作业
现在,小a需要安排他的学期计划,如果小a的学期中存在一天xx,在这之前的xx天中,他没写作业的天数 - 写作业的天数⩾k⩾k,那么老师就会把它开除,我们称这是一种不合法的方案
小a想知道他有多少种合法的方案
输入描述:
第一行三个整数n,k,pn,k,p,pp表示对pp取模
输出描述:
一个整数表示答案
示例1
输入
复制
2 1 100007
输出
复制
2
说明
总共有2n=42n=4天 合法的方案有 写了 没写 写了 没写 写了 写了 没写 没写 注意:没写 写了 没写 写了 是一种不合法的方案,因为在第一天时没写的天数-写了的天数⩾1⩾1
示例2
输入
复制
10 5 10000007
输出
复制
169252
备注:
1⩽n,k⩽106,p⩽109+71⩽n,k⩽106,p⩽109+7 不保证pp为质数!
这个要推出公式,更重要的是会写组合数取模代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e6+5;
bool prime[maxn];
int p[maxn];
int cnt;
int mod;
void isprime()
{
cnt=0;
memset(prime,true,sizeof(prime));
for(int i=2;i<maxn;i++)
{
if(prime[i])
{
p[cnt++] = i;
for(int j=i+i;j<maxn;j+=i)
prime[j] = false;
}
}
}
ll quick_mod(ll a,ll b,ll m)
{
ll ans=1;
while(b)
{
if(b&1)
{
ans=ans*a%m;
}
b>>=1;
a=a*a%m;
}
return ans;
}
ll fun(ll n,ll m)
{
ll ans=0;
while(n)
{
ans+=n/m;
n=n/m;
}
return ans;
}
ll solve(ll n,ll m,ll pp)
{
ll ans=1;
for(int i=0;i<cnt&&p[i]<=n;i++)
{
ll x=fun(n,p[i]);
ll y=fun(n-m,p[i]);
ll z=fun(m,p[i]);
ll xx=x-y-z;
ans=ans*quick_mod(p[i],xx,pp)%pp;
}
return ans;
}
int main()
{
isprime();
int t;
ll n,m,pp;
scanf("%lld%lld%lld",&n,&m,&pp);
printf("%lld\n",(solve(n*2,n,pp) - solve(n*2,m+n,pp)+pp)%pp);
return 0;
}