luogu P1450 [HAOI2008]硬币购物

luogu P1450 [HAOI2008]硬币购物

题目大意比较清楚

题解

这题正向做不是很好做
考虑求问题的补集
求不合法的方案数,
发现可以用容斥来解决
然后用总的方案数减去不合法的即可

先做一个完全背包 f [ 1...100000 ] f[1...100000] f[1...100000]
假设当前硬币的面值是 a [ i ] a[i] a[i],个数限制是 g s [ i ] gs[i] gs[i]
然后不合法的方案数就是 f [ s − a [ i ] ∗ ( g s [ i ] + 1 ) ] f[s - a[i] *(gs[i] +1)] f[sa[i](gs[i]+1)]
然后再做容斥就可以了
具体看代码吧

code:


#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[15], gs[15], f[200005], n, t;
signed main(){
	scanf("%lld%lld%lld%lld%lld", &a[1], &a[2], &a[3], &a[4], &t);
	f[0] = 1;
	for(int j = 1; j <= 4; j ++) 
		for(int i = 0; i <= 100000; i ++) 
			f[i + a[j]] += f[i];//先做一个完全背包
	while(t --){
		scanf("%lld%lld%lld%lld%lld", &gs[1], &gs[2], &gs[3], &gs[4], &n);
		int ans = f[n];
		for(int i = 1; i < (1 << 4); i ++){
			int o = 1, oo = 0;
			for(int j = 1; j <= 4; j ++) if(i & (1 << (j - 1))) o = - o, oo += (long long)(gs[j] + 1) * a[j];//容斥
			if(oo <= n) ans += o * f[n - oo];//容斥
		}
		printf("%lld\n", ans);
	}
	
	return 0;
}

我觉得我需要智力康复一下QWQ

posted @ 2019-09-27 15:24  lahlah  阅读(14)  评论(0编辑  收藏  举报