SSL-ZYC 方案数

题目大意:
老师今天买了好多黑色和白色巧克力(可以认为数量无限)来奖励同学们,让同学们自己来拿巧克力,但有如下限制:
1、每个人最少拿一块巧克力;
2、每个人只能拿一种颜色的巧克力;
3、拿黑色巧克力的人不少于C 个;
4、第i 个人最多拿a[i]个黑巧克力,或最多拿b[i]个白巧克力。
请问N(编号1~N)个同学拿巧克力可能的方案数是多少?(取模10007即可)
100%的数据:1≤N≤100000,1≤C≤20,1≤a[i],b[i]≤10^9。


思路:
明显是一道完全背包的问题。(巧克力数量是无限的)
但是这道题的数据很坑,如果我们直接打完全背包,时间复杂度则是O(n^2),很明显会超时。
那么我们可以换一种思路:
先求出拿巧克力的总方法,再用完全背包求出小于c人取黑巧克力的方案数(时间复杂度O(nc)),最后用总方法减去小于c人取黑巧克力的方案数,就是我们要求的答案。
状态转移方程:(f[i][j]=f[i-1][j-1]*a[i]+f[i-1][j]*b[i])


代码:

#include <cstdio>
#include <iostream>
using namespace std;

int f[100001][21],a[100001],b[100001],sum,ans;
int n,m;

int main()
{
    freopen("fas.in","r",stdin);
    freopen("fas.out","w",stdout);
    scanf("%d%d",&n,&m);
    sum=1;
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&b[i]);
        sum=sum*(a[i]+b[i])%10007;  //计算总方案数
    }
    f[0][0]=1;
    for (int i=1;i<=n;i++) f[i][0]=(f[i-1][0]*(b[i]%10007))%10007;  //初始化
    for (int i=1;i<=n;i++)
     for (int j=1;j<=m;j++)  //完全背包
        f[i][j]=((f[i-1][j-1]*(a[i]%10007))%10007+(f[i-1][j]*(b[i]%10007))%10007)%10007;  //当i个人中j个人取了黑巧克力时的方案数
     for (int i=0;i<m;i++)
     {
        sum=(sum-f[n][i])%10007;  //计算总方案数
        if(sum<0) sum+=10007;
     }
    printf("%d\n",sum);
    return 0;
}
posted @ 2018-01-31 07:34  全OI最菜  阅读(86)  评论(0编辑  收藏  举报