BZOJ-1042:硬币购物(背包+容斥)
题意:硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次有多少种付款方法。
思路:这么老的题,居然今天才做到...背包的复杂度是比较高的。 加上tot次询问会爆炸。能不能预处理,然后容斥得到答案呢?
先求一个完全背包,求出方案数,dp[]。
然后对于具体的询问,减去不合法的情况 。
对于c[i],它发贡献是dp[S-c[i]*(d[i]+1)];
那么会重复减,所以又加回来...
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) #define ll long long using namespace std; const int maxn=200010; ll dp[maxn],c[5],d[5],S,ans; void dfs(int pos,int num,ll sum) { if(pos==5){ if(sum>=0) { if(num&1) ans-=dp[sum]; else ans+=dp[sum]; } return ; } dfs(pos+1,num+1,sum-c[pos]*(d[pos]+1)); dfs(pos+1,num,sum); } int main() { int Q; rep(i,1,4) scanf("%lld",&c[i]); dp[0]=1; rep(i,1,4) rep(j,c[i],100000) dp[j]+=dp[j-c[i]]; scanf("%d",&Q); while(Q--){ rep(i,1,4) scanf("%lld",&d[i]); scanf("%lld",&S); ans=0; dfs(1,0,S); printf("%lld\n",ans); } return 0; }
It is your time to fight!