洛谷P1450 [HAOI2008]硬币购物(背包问题,容斥原理)

洛谷题目传送门
我实在是太弱了,第一次正儿八经写背包DP,第一次领会如此巧妙的容斥原理的应用。。。。。。

对每次询问都做一遍多重背包,显然T飞,就不考虑了

关键就在于每次询问如何利用重复的信息

我这么弱,当然是想不到容斥原理的啦

暂且先当成完全背包,每种硬币可使用无限次,预处理f数组,f[i]等于买价值i的东西的总方案数

然后就要从中减去不合法的。首先肯定会有一种硬币超额使用,第j中硬币等于说强制选了dj+1个,剩下的依然随便选,那么第
j种硬币超额的不合法的方案数等于f[s(dj+1)cj],于是从答案里减去j=14f[s(dj+1)cj]

还要注意,第一种第二种都超额、第一种第三种都超额、第一种第四种都超额、第二种第三种都超额、第二种第四种都超额、第三种第四种都超额的方案在上一步中都被减了两次,所以额外都加一次回来。。。。。。(接着把容斥做下去就不说了)

复杂度降到O(4maxs+4×24tot),轻松通过

注意开longlong就好啦

#include<cstdio>
#define R register
typedef long long LL;
const int S=100009;
LL f[S]={1ll};
int main(){
	R int c[4],d[4],tot,i,j,k,now,s,ss,tmp;
	R LL ans;
	for(j=0;j<4;++j)scanf("%d",&c[j]);
	scanf("%d",&tot);
	for(j=0;j<4;++j)
		for(i=c[j];i<S;++i)
			f[i]+=f[i-c[j]];//完全背包预处理
	while(tot--){
		for(j=0;j<4;++j)scanf("%d",&d[j]);
		scanf("%d",&s);
		ans=f[s];
		for(ss=1;ss<=15;++ss){//二进制数枚举集合,容斥
			now=s;
			for(tmp=ss,j=k=0;tmp;tmp>>=1,++j)
				if(tmp&1)k^=1,now-=(d[j]+1)*c[j];
                //注意k的作用,判断奇偶
			if(now>=0)k?ans-=f[now]:ans+=f[now];
		}
		printf("%lld\n",ans);
	}
	return 0;
}
posted @   Flash_Hu  阅读(217)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示
剑桥
17:14发布
剑桥
17:14发布
5°
西风
7级
空气质量
相对湿度
34%
今天
多云
-3°/5°
周六
-1°/3°
周日
-2°/7°