Description
(n)种花((nleq20)),每种花不超过(f_i)朵,问总数为(s)的方案数
Solution
容斥+组合数学
因为(n)很小,考虑容斥,即无任何限制的方案数(-)至少一种不满足的方案数(+)至少两种不满足的方案数。。。
如何求无任何限制的方案数?问题可以转化为(x_1+x_2+dots+x_n=s)的非负解数,用组合数学的隔板法解决,方案数为(C^{n-1}_{s+n-1})。
如何求至少一种不满足的方案数?答案为(sum_{i=1}^{n}C_{s-f_i-1+n-1}^{n-1})。因为至少要让一种花的数量在限制外。
那么解法呼之欲出了。每到一种花递归求解该种强制不选或随意的方案数,强制不选就使(s-=(f_i+1)),随意就将(s)继续递归,同时维护容斥系数即可。
时间复杂度(O(n2^n))
细节见代码:
#include<bits/stdc++.h>
#define int long long
#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
const int N=25, P=1e9+7;
void add(int &a, int b){a=a+b>=P?a+b-P:a+b;}
int mul(int a, int b){return a*b-a*b/P*P;}
int f[N], inv[N], n, s, sum;
inline int read()
{
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
int C(int n, int m)
{
if (n<m) return 0; int res=1;
rep(i, 1, m) res=mul(res, mul((n-i+1)%P, inv[i]));
return res;
}
int dfs(int i, int s, int sign)
{
if (s<0) return 0;
if (i==n+1) return sign*C(s+n-1, n-1);
int ans=dfs(i+1, s, sign);
add(ans, dfs(i+1, s-f[i]-1, 0-sign));
return ans<0?ans+P:ans;
}
signed main()
{
n=read(); s=read();
rep(i, 1, n) f[i]=read(), sum+=f[i];
inv[1]=1; rep(i, 2, n) inv[i]=mul(inv[P%i], P-P/i);
printf("%d
", sum<s?0:dfs(1, s, 1));
return 0;
}