题解 CF451E 【Devu and Flowers】
贡献一个只用到基础组合数学+容斥的做法
假设每种花都是无穷多的。那么,方案数显然相当于 \(s\) 个小球,分 \(k\) 组,每组至少 \(0\) 个的方案数。
考虑初始加入 \(n\) 个小球,按每组至少 \(1\) 个进行插空分配,之后再抽走。方案数显然为 \(\dbinom{s+n-1}{n-1}\) 。
当然,这个分法显然有问题,因为每种花并不是无穷多的,这种分法可能导致一些花比限制条件更多。
现考虑某集合 \(S\) ,集合中的每种花都严格多于限制条件。则对于集合中的某种花 \(i\) ,选择第 \(i\) 种花的数量 \(>f_i\) ,即 \(\geq f_i+1\) 。
因此,不妨设 \(\displaystyle f_S=\sum_{i\in S}(f_i+1)\) 。
考虑集合 \(S\) 中每种花都严格多于限制条件的方案数,等价于先将 \(s\) 个小球中选出 \(f_S\) 个,剩余的部分分 \(k\) 组,每组至少 \(0\) 个。分配后,再将 \(f_S\) 个按需分配回去。因此,方案数为 \(\dbinom{s-f_S+n-1}{n-1}\) 。
最后求每种花都不多于限制条件的方案数,则进行容斥:
有 \(0\) 个超限的 \(-\) 有 \(1\) 个超限的 \(+\) 有 \(2\) 个超限的 $\cdots $
\(\displaystyle ans=\sum_{S}(-1)^{|S|}\dbinom{s-f_S+n-1}{n-1}\)
由于 \(n-1\leq 19\) ,直接暴力展开组合数即可,复杂度为 \(O(n\cdot 2^n)\)
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=1e9+7;
inline ll fpow(ll a,ll x) { ll ans=1; for(a%=MOD;x;x>>=1,a=a*a%MOD) if(x&1) ans=ans*a%MOD; return ans; }
inline ll inv(ll a) { return fpow(a,MOD-2); }
//快速幂 & 逆元
ll n,s,f[1<<20],invn;//invn 为 (n-1)! 的逆元
inline ll C(ll n,ll m){
if(m<0||m>n) return 0;
ll ans=1;
for(int i=1;i<=m;i++)
ans=(n-i+1)%MOD*ans%MOD;
return ans*invn%MOD;
}
inline ll calc(ll S){
if(S&(S-1)) f[S]=f[S^(S&(-S))]+f[S&(-S)];
return C(s-f[S]+n-1,n-1);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n>>s;
invn=1;
for(int i=1;i<=n-1;i++) invn=invn*i%MOD;
invn=inv(invn);
for(int i=1,s=1;i<=n;i++,s<<=1) cin>>f[s],++f[s];
ll Ans=0;
for(int i=0;!(i>>n);i++)
Ans=( Ans+calc(i)*((__builtin_popcount(i>>10)+__builtin_popcount(i&1023)&1)?-1:1) )%MOD;
//__builtin_popcount(i) 可以直接算出 i 二进制中 1 的个数,但要 i 相对较小的时候
Ans=(Ans%MOD+MOD)%MOD;
cout<<Ans;
cout.flush();
return 0;
}