BZOJ 1042: [HAOI2008]硬币购物 容斥原理+背包
Description
硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买s
i的价值的东西。请问每次有多少种付款方法。
题解:
容斥原理公式:(源自百度百科)
十分喜欢这道题,真的非常巧妙.
不加限制,该题会变得特别模板.
有限制后,似乎正着求有些困难,我们就考虑反着求,即简单容斥一下.
我们先求出不加限制的方案数,再将不合法的方案数依次减掉.
考虑容斥原理:
总方案 - $\sum$ 1个不合法 + $\sum$ 2个不合法 - $\sum$ 3个不合法......
假设不加限制的总个数为 $N$ ,第一物品的限制为 $d$ (一共4个物品),那么当我们拿 $d+1$,$d+2$.... 个物品的时候开始不合法.
也就是说,不合法的情况下,我们至少拿 $d+1$ 个1物品,剩下的由 1,2,3,4随便拼凑.
而剩下的四个物品随便拼凑不就是 $DP[N-(d+1)*val_{1}]$ 嘛......
其余情况同理.
Code:
#include<bits/stdc++.h> #define setIO(s) freopen(s".in","r",stdin) #define maxn 1000000 #define N 200000 #define ll long long using namespace std; int c[5],d[5]; ll dp[maxn]; int main(){ //setIO("input"); for(int i=0;i<4;++i) scanf("%d",&c[i]); dp[0]=1; for(int i=0;i<4;++i) for(int j=c[i];j<=N;++j) dp[j]+=dp[j-c[i]]; int T; scanf("%d",&T); while(T--){ for(int i=0;i<4;++i) scanf("%d",&d[i]); int res; scanf("%d",&res); ll ans=0; for(int i=0;i<16;++i){ int cnt=0,cur=res; for(int j=0;j<4;++j) if((i>>j)&1) cnt^=1,cur-=(d[j]+1)*c[j]; if(cur<0) continue; if(cnt) ans-=dp[cur]; else ans+=dp[cur]; } printf("%lld\n",ans); } return 0; }