BZOJ 1042 硬币购物

Posted on 2016-12-19 10:39  ziliuziliu  阅读(169)  评论(0编辑  收藏  举报

先不考虑限制,那么有dp[i]表示i元钱的方案数。

然后考虑限制,发现可以容斥。

其实整个题就是两个容斥原理。感觉出的蛮好的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100500
using namespace std;
long long f[maxn],c[5],d[5],n,s,ans;
void pre_dp()
{
    f[0]=1;
    for (long long i=1;i<=maxn-500;i++)
    {
        for (long long j=1;j<=4;j++)
            if (i>=c[j]) f[i]+=f[i-c[j]];
        for (long long j=1;j<=4;j++)
            for (long long k=j+1;k<=4;k++)
                if (i>=c[j]+c[k]) f[i]-=f[i-c[j]-c[k]];
        for (long long j=1;j<=4;j++)
            for (long long k=j+1;k<=4;k++)
                for (long long l=k+1;l<=4;l++)
                    if (i>=c[j]+c[k]+c[l]) f[i]+=f[i-c[j]-c[k]-c[l]];
        if (i>=c[1]+c[2]+c[3]+c[4]) f[i]-=f[i-c[1]-c[2]-c[3]-c[4]];
    }
}
void work()
{
    ans=0;ans+=f[s];
    for (long long i=1;i<=4;i++) if (s>=d[i]*c[i]) ans-=f[s-d[i]*c[i]];
    for (long long i=1;i<=4;i++)
        for (long long j=i+1;j<=4;j++)
            if (s>=d[i]*c[i]+d[j]*c[j]) ans+=f[s-d[i]*c[i]-d[j]*c[j]];
    for (long long i=1;i<=4;i++)
        for (long long j=i+1;j<=4;j++)
            for (long long k=j+1;k<=4;k++)
                if (s>=d[i]*c[i]+d[j]*c[j]+d[k]*c[k]) ans-=f[s-d[i]*c[i]-d[j]*c[j]-d[k]*c[k]];
    if (s>=d[1]*c[1]+d[2]*c[2]+d[3]*c[3]+d[4]*c[4]) ans+=f[s-(d[1]*c[1]+d[2]*c[2]+d[3]*c[3]+d[4]*c[4])];
    printf("%lld\n",ans);
}
int main()
{
    for (long long i=1;i<=4;i++) scanf("%lld",&c[i]);
    scanf("%lld",&n);
    pre_dp();
    for (long long i=1;i<=n;i++)
    {
        for (long long j=1;j<=4;j++) {scanf("%lld",&d[j]);d[j]++;}
        scanf("%lld",&s);
        work();
    }
    return 0;
}