BZOJ1042 || 洛谷P1450 [HAOI2008]硬幣購物【揹包+容斥】
阿新 • • 發佈:2018-12-19
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
首先假如沒有物品個數的限制,那麼這就是一個完全揹包,這樣的情況可以直接預處理 現在考慮加入限制,當前中包含的超過限制的方案數為 相當於先強制往包中放入個,剩餘的容積不管怎麼放都已經是不合法的了
先令,然後 但是這時候我們又發現同時不合法的方案數多減了,我們再加回來 這時叒發現同時不合法的方案數多加了,我們再減掉 以及叕加回四個同時不合法的方案數
等等,這不就是容什麼斥 一般容斥都可以轉化成二進位制簡化運算 複雜度約為
#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;
}