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; 
}

  

posted @ 2019-05-22 14:39  EM-LGH  阅读(...)  评论(... 编辑 收藏