[BZOJ1042][HAOI2008]硬币购物

bzoj
luogu

sol

首先考虑如果没有硬币数量的限制该怎么做。
直接上背包吧。
直接上背包存在的问题就是不知道硬币的使用数量有没有超出限制。
那就容斥一波。
考虑每种硬币,强制其超出限制,也就是强制先使用\(d_i+1\)个该种硬币,然后剩下队随便选。
时间复杂度是\(O(V+q*2^4)\),其中\(V\)是背包上限(十万即可)。

code

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
ll gi()
{
	ll x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 1e5+5;
ll c[4],f[N],q,d[4],s;
ll k[16]={1,-1,-1,1,-1,1,1,-1,-1,1,1,-1,1,-1,-1,1};
int main()
{
	for (int i=0;i<4;++i) c[i]=gi();
	f[0]=1;
	for (int i=0;i<4;++i)
		for (int j=c[i];j<=100000;++j)
			f[j]+=f[j-c[i]];
	q=gi();
	while (q--)
	{
		for (int i=0;i<4;++i) d[i]=gi();
		s=gi();ll ans=0;
		for (int i=0;i<16;++i)
		{
			ll sum=0;
			if (i&1) sum+=(d[0]+1)*c[0];
			if (i&2) sum+=(d[1]+1)*c[1];
			if (i&4) sum+=(d[2]+1)*c[2];
			if (i&8) sum+=(d[3]+1)*c[3];
			ans+=k[i]*(sum>s?0:f[s-sum]);
		}
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2018-03-27 14:11  租酥雨  阅读(173)  评论(0编辑  收藏  举报