BZOJ4710 [Jsoi2011]分特产 容斥原理
4710: [Jsoi2011]分特产
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 601 Solved: 389
[Submit][Status][Discuss]
Description
JYY 带队参加了若干场ACM/ICPC 比赛,带回了许多土特产,要分给实验室的同学们。
JYY 想知道,把这些特产分给N 个同学,一共有多少种不同的分法?当然,JYY 不希望任
何一个同学因为没有拿到特产而感到失落,所以每个同学都必须至少分得一个特产。
例如,JYY 带来了2 袋麻花和1 袋包子,分给A 和B 两位同学,那么共有4 种不同的
分配方法:
A:麻花,B:麻花、包子
A:麻花、麻花,B:包子
A:包子,B:麻花、麻花
A:麻花、包子,B:麻花
Input
输入数据第一行是同学的数量N 和特产的数量M。
第二行包含M 个整数,表示每一种特产的数量。
N, M 不超过1000,每一种特产的数量不超过1000
Output
输出一行,不同分配方案的总数。由于输出结果可能非常巨大,你只需要输出最终结果
MOD 1,000,000,007 的数值就可以了。
Sample Input
5 4
1 3 3 5
1 3 3 5
Sample Output
384835
昨天多校容斥原理做的很不顺手,没有理解容斥的精髓,遂找到该题练手。
题解:
m个物品分给n人-m个物品分给n-1人×c(n,1)+m个物品分给n-2人×c(n-2).........一直容斥下去
将m个物品分给n人=c(m+n-1,n-1) -----为避免部分人分不到物品的讨论,假设初始状态没人均有1个物品,问题转化为m+n个物品分给n人,且保证每人均有物品,有隔板法易得c(m+n-1,n-1)
1 #include <bits/stdc++.h> 2 #include <cstdio> 3 #include <iostream> 4 #include <cmath> 5 6 using namespace std; 7 const int mod = 1e9+7; 8 typedef long long ll; 9 ll fac[2005],ifac[2005];//fac[n]表示n!,ifac[n]表示n!的逆元 10 ll ans; 11 ll a[1005]; 12 int n,m; 13 ll quick(ll x,ll y) 14 { 15 ll res = 1; 16 while(y) 17 { 18 if (y&1) res = (res * x) % mod; 19 x = x * x % mod; 20 y >>= 1; 21 } 22 return res % mod; 23 } 24 void init(){ 25 fac[0] = ifac[0] = 1; 26 for (int i = 1;i < 2002;++i){ 27 fac[i] = fac[i-1] * i % mod; 28 ifac[i] = quick(fac[i],mod-2); 29 } 30 } 31 32 ll c(ll n,ll k){ 33 if (k == 0) return 1; 34 return fac[n] * ifac[k] % mod * ifac[n-k] % mod; 35 } 36 37 ll fen(ll num){ 38 ll res= 1; 39 for (int i = 1;i <= m;++i){ 40 res = res * c(a[i]+num-1,num-1) % mod; 41 } 42 return res; 43 } 44 45 int main(){ 46 init(); 47 scanf("%d%d",&n,&m); 48 for (int i = 1;i <= m;++i) scanf("%lld",a+i); 49 ans = 0; 50 for (int i = 0;i < n;++i){ 51 ans = (ans + c(n,i) * fen(n-i) % mod * ((i & 1) ? -1ll : 1ll) + mod) % mod; 52 } 53 printf("%lld\n",ans); 54 return 0; 55 }