花
问题描述:
商店里出售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;
}