牛客 组合数取模问题

链接: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;
}

猜你喜欢

转载自blog.csdn.net/lanshan1111/article/details/86602639