以后每天一个小专题
问题 B: 扑克牌
时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]题目描述
从一副含有n(n≤10000)张的扑克牌[显然每张扑克牌都不相同]中,分给m(m≤100)个人,第i个人得到ai (0≤ai≤100)张牌,求一共有几种分法,这个数可能非常大,请输出此数模10007后的结果。
输入
第一行两个整数 为 n m
第二行 m个整数 ai
输出
一个整数,表示有多少种分法
样例输入 Copy
【样例1】 5 2 3 1 【样例2】 20 19 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
样例输出 Copy
【样例1】 20 【样例2】 8707
其实就是一模板题, 开始用暴力法无果,然后其他各种乱写,后来大佬告诉我是逆元,看了之后觉得,太强了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 500;
const int mod = 10007;
int n,m,sum;
int fac[N],inv[N];
ll qmod(ll n, ll k)
{
ll res = 1;
while(k)
{
if(k&1)
res=res*n%mod;
k>>=1;
n = n*n%mod;
}
return res;
}
ll C(ll n, ll m)//Cnm
{
return fac[n]*inv[m]%mod *inv[n-m]%mod;
}
void init(int n)
{
fac[0]=1;
for(int i=1;i<=n;i++)
{
fac[i]=fac[i-1]*i%mod; //求i!
}
inv[n]=qmod(fac[n],mod-2);
for(int i=n-1;i>=0;i--)
{
inv[i]=inv[i+1]*(i+1)%mod; //指的逆元的值,线性的
}
}
int main()
{
init(10000);
ll ans=1;
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
int a;
scanf("%d",&a);
ans=(ans*C(n,a)%mod);
n-=a;
if(n<0)
{
printf("0");
return 0;
}
}
printf("%lld\n",ans);
return 0;
}