bzoj1042: [HAOI2008]硬币购物

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 using namespace std;
 7 #define maxn 100005
 8 #define maxk 16
 9 int c[5],tot,n,d[5],num[maxk];
10 typedef long long ll;
11 ll f[maxn],ans;
12 void read(int &x){
13     x=0; int f=1; char ch;
14     for (ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') f=-1;
15     for (;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; x*=f;
16 }
17 ll calc(int x,int y){
18     for (int i=0;i<4;i++) if ((x>>i)&1) y-=c[i+1]*(d[i+1]+1);
19     if (y<0) return 0;
20     if (num[x]&1) return -f[y];
21     else return f[y];
22 }
23 int lowbit(int x){return x&(-x);}
24 int main(){
25     for (int i=1;i<16;i++) num[i]=num[i-lowbit(i)]+1;
26     for (int i=1;i<=4;i++) read(c[i]);
27     memset(f,0,sizeof(f)),f[0]=1;
28     for (int i=1;i<=4;i++){
29         for (int j=c[i];j<=100000;j++){
30             f[j]+=f[j-c[i]];
31         }
32     }
33     read(tot);
34     for (;tot;--tot){
35         for (int i=1;i<=4;i++) read(d[i]); read(n);
36         ans=0; for (int i=0;i<16;i++) ans+=calc(i,n);
37         printf("%lld\n",ans);
38     }
39     return 0;
40 }
View Code

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1042。

题目大意:硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买s
i的价值的东西。请问每次有多少种付款方法。

Input 第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s,其中di,s<=100000,tot<=1000。

Output  每次的方法数。

 做法:初看这题,对于每个询问,就是一个多重背包嘛,但是复杂度较高,过不了此题,我们发现这跟其他的背包有一点不同,就是物品只有4中,我们发现可以有指数级做法,想到了容斥原理,怎么容斥呢?显然,如果每个询问都没有限制,我们只需要O(n)的复杂度预处理,每次询问时便可以O(1)的回答了。但是怎么转化呢,这题中限制了上界不好转化,我们可以用容斥的一般式转化为补集,然后就变成了有部分变量规定了下界限制,我们在等式两边同时减去下界和即可,转化为了可以预处理出来的无限制的背包问题。容斥的注意事项,我们用一个4位的二进制数上各位的0/1来决定取还是不取,O(n)预处理出每个4位二进制数上1的个数,num[i]=num[i-lowbit(i)]+1,这很显然嘛。。

容斥原理+动态规划。

posted @ 2016-06-23 09:44  oyzx~  阅读(...)  评论(... 编辑 收藏