花(唯一分解定理+排列组合+快速幂)

问题描述:
商店里出售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
思路:
组合数裸题,就是要求出c(n,m)%p
由公式得:c(n,m)%p=!n*!m/!(n-m)%p
首先会想到化简,分子分母能约的约掉,但是剩下的乘起来会爆(数据有点大。。。),这时候取模的话,答案显然是错误的,因为涉及到了除法,所以这种方案不可行!
然后会想到费马小定理,求出逆元,进而得出答案,显然当p为合数是无解!
最后无奈,分子分母分解质因数,然后约分,最后计算答案,加剪枝(否则80分,超时!)

#include<iostream>
#include<cstdio>
#include<algorithm>
#define lon long long
using namespace std;
const int maxn=1000010;
int n,m,p,ans=1,sum,a[maxn],prime[maxn],P[maxn];
bool flag[maxn];
void prepare()
{
    for(int i=2;i<=1000000;i++)
    if(!flag[i])
    {
        prime[++sum]=i;
        P[i]=sum;
        for(int j=i+i;j<=1000000;j+=i)
        flag[j]=1;
    }
}
void work1(int x)
{
    for(int i=1;i<=sum;i++)
    {
        if(x%prime[i]==0)
        {
            int tmp=0;
            while(x%prime[i]==0)
            {
                x=x/prime[i];
                tmp++;
            }
            a[i]+=tmp;
        }
        if(x==1) break;
        if(!flag[x])//剪枝
        {
            a[P[x]]++;
            break;
        }
    }
}
void work2(int x)
{
    for(int i=1;i<=sum;i++)
    {
        if(x%prime[i]==0)
        {
            int tmp=0;
            while(x%prime[i]==0)
            {
                x=x/prime[i];
                tmp++;
            }
            a[i]-=tmp;
        }
        if(x==1) break;
        if(!flag[x])//剪枝
        {
            a[P[x]]--;
            break;
        }
    }
}
lon quick_power(lon x,lon y,lon z)//快速幂
{
    lon tmp=1;
    while(y)
    {
        if(y&1)
        tmp=tmp*x%z;
        x=x*x%z;
        y>>=1;
    }
    return tmp;
}
int main()
{
    cin>>n>>m>>p;
    prepare();//筛出素数,顺便预处理质因数分解
    for(int i=2;i<=n;i++)//对!n质因数分解,作为分子
    work1(i);
    for(int i=2;i<=m;i++)//对!m质因数分解,作为分母
    work2(i);
    for(int i=2;i<=n-m;i++)//对!(n-m)质因数分解,作为分母
    work2(i);
    for(int i=1;i<=sum;i++)
    if(a[i])
    ans=ans*quick_power(prime[i],a[i],p)%p;
    cout<<ans;
    return 0;
}
posted @ 2016-10-12 20:44  抽空的太阳  阅读(174)  评论(0编辑  收藏  举报