bzoj1042: [HAOI2008]硬币购物(DP+容斥)

  1600+人过的题排#32还不错嘿嘿

  浴谷夏令营讲过的题,居然1A了

  预处理出f[i]表示购买价值为i的东西的方案数

  然后每次询问进行一次容斥,答案为总方案数-第一种硬币超限方案-第二种超限方案-第三种超限方案-第四种超限方案+第一种和第二种硬币超限方案+...

  第i种硬币超限方案就是f[s-c[i]*(d[i]+1)],其他的类推一下就行了

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio> 
#include<algorithm>
#define ll long long 
using namespace std;
const int maxn=100010,inf=1e9;
int n,mx;
ll ans;
int c[5],d[maxn][5],s[maxn];
ll f[maxn];
void read(int &k)
{
    int f=1;k=0;char c=getchar();
    while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
    while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();
    k*=f;
}
void dfs(int fir,int dep,int sum,int last)
{
    for(int i=last;i<=4;i++)
    {
        if(sum+c[i]*(d[fir][i]+1)>s[fir])continue;
        if(dep&1)ans-=f[s[fir]-sum-c[i]*(d[fir][i]+1)];
        else ans+=f[s[fir]-sum-c[i]*(d[fir][i]+1)];
        dfs(fir,dep+1,sum+c[i]*(d[fir][i]+1),i+1);
    }
}
int main()
{
    for(int i=1;i<=4;i++)read(c[i]);read(n);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=4;j++)read(d[i][j]);
        read(s[i]);mx=max(mx,s[i]);
    }
    f[0]=1;
    for(int i=1;i<=4;i++)
    for(int j=c[i];j<=mx;j++)
    f[j]+=f[j-c[i]];
    for(int i=1;i<=n;i++)
    {
        ans=f[s[i]];
        dfs(i,1,0,1);
        printf("%lld\n",ans);
    }
    return 0;
}
View Code
posted @ 2017-08-30 22:12  Sakits  阅读(...)  评论(... 编辑 收藏