1. 程式人生 > >BZOJ1042 || 洛谷P1450 [HAOI2008]硬幣購物【揹包+容斥】

BZOJ1042 || 洛谷P1450 [HAOI2008]硬幣購物【揹包+容斥】

Time Limit: 10 Sec Memory Limit: 162 MB

Description

硬幣購物一共有4種硬幣。面值分別為c1,c2,c3,c4。某人去商店買東西,去了tot次。每次帶di枚ci硬幣,買si的價值的東西。請問每次有多少種付款方法。

Input

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

Output

每次的方法數

題目分析

第一眼單調佇列優化多重揹包,但再一看資料範圍emmmm

首先假如沒有物品個數的限制,那麼這就是一個完全揹包,這樣的情況可以直接預處理 現在考慮加入限制,當前d

p[Si]dp[S_i]中包含的超過did_i限制的方案數為dp[Sici(ci+1)]dp[S_i-c_i*(c_i+1)] 相當於先強制往包中放入di+1d_i+1cic_i剩餘Sici(ci+1)S_i-c_i*(c_i+1)的容積不管怎麼放都已經是不合法的了

先令ans=dp[Si]ans=dp[S_i],然後ans=i=14dp[Sici(di+1)]ans-=\sum_{i=1}^4dp[S_i-c_i(d_i+1)]

dp[Sici(di+1)] 但是這時候我們又發現ci,cj(i!=j)c_i,c_j(i!=j)同時不合法的方案數多減了,我們再加回來 這時叒發現ci,cj,ck(i!=j!=k)c_i,c_j,c_k(i!=j!=k)同時不合法的方案數多加了,我們再減掉 以及叕加回四個同時不合法的方案數

等等,這不就是容什麼斥 一般容斥都可以轉化成二進位制簡化運算 複雜度約為O(4maxn+T24)O(4*maxn+T*2^4)

#include<iostream>
#include<vector>
#include<algorithm> #include<queue> #include<cstring> #include<cstdio> #include<cmath> using namespace std; typedef long long lt; int read() { int f=1,x=0; char ss=getchar(); while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();} while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();} return f*x; } const int maxn=100010; int T,n; int ci[5],di[5]; lt dp[maxn],S; int main() { for(int i=1;i<=4;++i) ci[i]=read(); dp[0]=1; for(int i=1;i<=4;++i) for(int j=ci[i];j<maxn;++j) dp[j]+=dp[j-ci[i]]; T=read(); while(T--) { for(int i=1;i<=4;++i) di[i]=read(); S=read(); lt res=dp[S]; for(int i=1;i<=(1<<4)-1;++i) { int tt=S,cnt=0; for(int j=1;j<=4;++j) if(i&(1<<j-1)) cnt++,tt-=ci[j]*(di[j]+1); if(tt<0) continue; if(cnt&1) res-=dp[tt]; else res+=dp[tt]; } printf("%lld\n",res); } return 0; }