Fork me on GitHub

花(cnm加强)

【问题描述】
商店里出售n种不同品种的花。为了装饰桌面,你打算买m支花回家。你觉得放两支一样的花很难看,因此每种品种的花最多买1支。求总共有几种不同的买花的方案?答案可能很大,输出答案mod p的值。

【输入格式】
一行3个整数n,m,p,意义如题所述。

【输出格式】
一个整数,表示买花的方案数。
【输入输出样例1】
4 2 5
1
【输入输出样例1说明】
用数字1,2,3,4来表示花的种类的话,4种花里买各不相同的2支的方案有(1,2)、(1,3)、(1,4)、(2,3)、(2,4)、(3,4),共6种方案,模5后余数是1。

【数据范围】
对于30%的数据,n,m≤10
对于50%的数据,n,m≤1000
对于80%的数据,1≤m≤n≤50,000
对于100%的数据,1≤m≤n≤1,000,000,p≤1,000,000,000

n^2的杨辉三角肯定不行。
因为p不一定是质数,所以用费马小定理求逆元也不行。
只能用分解质因数。
数据很强,要用到快速幂,线性质数筛。
注意剪枝和利用开方降低时间复杂度。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define LL long long
using namespace std;
LL n,m,p;
LL ans;
int num[1000009],prime[1000009],cnt;
bool notp[1000009];
void Prime()
{
    notp[0]=1,notp[1]=1;
    for(LL i=2;i<=n;i++)
    {
        if(!notp[i]) prime[++cnt]=i;

        for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
        {
            notp[i*prime[j]]=1;
            if(i%prime[j]==0) break;
        }
    }
}//线性筛素数
LL Fast_pow(LL x,LL k)
{
    LL a1=1;
    x%=p;
    while(k)
    {
        if(k%2) a1=a1*x%p;
        x=(x*x)%p;
        k/=2;
    }
    return a1;
}
void tear(LL x,int d)
{
    LL r=(sqrt(x)+0.5);
    for(LL i=1;i<=cnt;i++)
    {
        if(!notp[x]) {num[x]+=d;return;}  //剪枝
        LL k=prime[i];
        if(k>r) break;//剪枝
        while(x%k==0)
        {
            num[k]+=d;
            x/=k;
        }
    }
    if(x>1) num[x]+=d;
}
void get_ans()
{
    ans=1;
    for(LL i=2;i<=n;i++)
    {
        if(num[i])
        {
            ans=(ans*Fast_pow(i,num[i]))%p;
        }
    }
}
int main()
{
    //freopen("flower.in","r",stdin);
    //freopen("flower.out","w",stdout);

    scanf("%lld%lld%lld",&n,&m,&p);

    Prime();

    for(LL i=m+1;i<=n;i++) tear(i,1);
    for(LL i=2;i<=n-m;i++) tear(i,-1);
    get_ans();
    printf("%lld",ans);
    return 0;
} 
posted @ 2017-09-24 17:48  primes  阅读(134)  评论(0编辑  收藏  举报