CF451E Devu and Flowers

题目传送门

Description

\(n\)种花\((n\leq20)\),每种花不超过\(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\n", sum<s?0:dfs(1, s, 1));
    return 0;
}

posted @ 2019-04-03 23:57  OIerC  阅读(112)  评论(0编辑  收藏  举报