洛谷 U140604 送花
洛谷 U140604 送花
题目背景
SeawaySeawa**y的老婆大人RoseRos**e要过生日啦!为了给老婆大人庆生,SeawaySeawa**y正打算给RoseRos**e送上一束盛开着的鲜花......
题目描述
认真起来的SeawaySeawa**y为了庄重地庆贺这个生日,大手一挥,买下了NN个花箱。在编号为ii的箱子里,有f[i]f[i]朵相同颜色的花。每个箱子里面的花的颜色两两不同。SeawaySeawa**y要从这些箱子里挑选SS朵花扎成花束,送给老婆大人。SeawaySeawa**y当然可以随意挑选SS朵花。但因为他一共买下了这么多花,如果只组一种花束未免浪费。但RoseRos**e也不喜欢一次性收到太多的花束。所以SeawaySeawa**y便开始思考:对于他买下的这些花,他有多少种挑选方式,使之能够组成SS朵花的花束?SeawaySeawa**y规定,两种挑选方式不同,当且仅当存在同一种颜色的花数量不同。由于答案可能很大,请输出答案对10^9+7109+7取模的结果。
输入格式
从文件blossom.inblossom.i**n中读入数据。
第一行两个整数N,SN,S,意义如题目描述所示。
第二行NN个整数,第ii个整数表示第ii个花箱里的花的数量f[i]f[i]。
输出格式
输出到文件blossom.outblossom.out中。
输出一行一个整数ansans,表示总挑选方案数量按要求取模后的结果。
题解:
题解:
排列组合这个地方的确比较难理解,来给大家用大白话讲一下。
多重集排列组合。
不妨反着来:对于有\(N\)种元素的多重集\(S\),选\(K\)个元素,注意是个不是种,的可行方案数。可以变成:现在有\(N\)个篮子,把\(K\)个元素扔进这些篮子里的方案数。
注意,这种是特殊情况,也就是说,每种元素无限多个可供挑选。
这样的话,用隔板法解决问题。
容易得出,答案也就是\(C_{N+K-1}^{N-1}\)。
解释一下,现在有\(K\)个元素,分成\(N\)堆,也就是要往里插入\(N-1\)块板。按理讲应该是\(C_{K+1}^{N-1}\),但是因为允许有空集,也就是不插,那么就相当于每块板子插进去之后又产生了新元素,所以是这个答案。
那么,根据多重集的限制,现在每种元素有一个数量上限,怎么办呢?
很简单,采用容斥原理。关于容斥原理,请见:
上限是“至多放\(f[i]\)个”,那么如果我往这个里面放\(f[i]+1\)个,是不是就不合法了?
把不合法的减去即可。
因为这道题N只有20,所以考虑状压,表示当前这个集合超没超。
最后根据容斥,如果当前状态1的个数是奇数,就要减去,是偶数,就要加上。
代码:
#include<cstdio>
#define ll long long
using namespace std;
const int maxn=25;
const int mod=1e9+7;
int n;
ll s;
ll f[maxn],inv[maxn];
ll calc(ll n,ll m)
{
if(n<0||m<0||n<m)
return 0;
n%=mod;
if(n==0||m==0)
return 1;
ll ret=1;
for(int i=0;i<m;i++)
ret=(ret*(n-i))%mod;
for(int i=1;i<=m;i++)
ret=(ret*inv[i])%mod;
return ret;
}//n中选m的组合数
ll qpow(ll a,ll b)
{
ll ret=1;
while(b)
{
if(b&1)
ret=(ret*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ret;
}
int main()
{
scanf("%d%lld",&n,&s);
for(int i=1;i<=n;i++)
scanf("%lld",&f[i]);
for(int i=1;i<=20;i++)
inv[i]=qpow(i,mod-2);
ll ans=calc(n+s-1,n-1)%mod;
for(int i=1;i<(1<<n);i++)
{
ll tot=n+s;
int p=0;
for(int j=0;j<n;j++)
if(i>>j&1)
{
p++;
tot-=(f[j+1]+1);
}
tot--;
if(p&1)
ans=(ans-calc(tot,n-1))%mod;
else
ans=(ans+calc(tot,n-1))%mod;
}
printf("%lld\n",(ans+mod)%mod);
return 0;
}