bzoj1042 [HAOI2008]硬币购物

来源:互联网 发布:算法工程师专业 编辑:程序博客网 时间:2024/06/05 06:13

题目

我们先考虑最最朴素的dp,像什么第几种硬币用了几枚的方法数,显然单次O(n^4)的吧,跑到宇宙毁灭的优秀算法233。

如何优化呢?显然,如果硬币没有限制的话,就是一个十分简单的dp了

f[0]=1;for(long long i=1;i<=4;i++)    for(long long j=c[i];j<=N;j++)        f[j]+=f[j-c[i]];

之后加上了数量限制,怎么办呢?

强行突破限制,上容斥定理,都没超出限制的方案数=无限制的-一个超的+两个超的。。。。

超的话,只需要超一个就可以了。

#include<bits/stdc++.h>#define N 100000using namespace std;long long ans,f[N+1];long long c[5],tot,d[5],s;inline char nc(){    static char buf[100000],*p1=buf,*p2=buf;    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}inline long long read(){    long long x=0,b=1;    char c=nc();    for(;!(c<='9'&&c>='0');c=nc())if(c=='-')b=-1;    for(;c<='9'&&c>='0';c=nc())x=x*10+c-'0';    return x*b;}inline void init(){    f[0]=1;    for(long long i=1;i<=4;i++)        for(long long j=c[i];j<=N;j++)            f[j]+=f[j-c[i]];    return;}inline void write(long long x){    char buf[25];    long long len=0;    if(!x)putchar('0');    else    {        while(x)buf[++len]=x%10+'0',x/=10;        for(long long i=len;i>=1;i--)putchar(buf[i]);    }    putchar('\n');}inline void get(long long pos,long long tmp,long long sum){    if(sum<0)return;    if(pos==5){        long long b=(tmp&1)?-1:1;        ans+=(long long)f[sum]*b;        return;    }    get(pos+1,tmp+1,sum-(d[pos]+1)*c[pos]);    get(pos+1,tmp,sum);    return;}int main(){    //freopen("in.txt","r",stdin);    for(long long i=1;i<=4;i++)c[i]=read();tot=read();    init();    for(long long i=1;i<=tot;i++)    {        for(long long j=1;j<=4;j++)d[j]=read();s=read();        ans=0,get(1,0,s),write(ans);    }    return 0;}