[HAOI2008]硬币购物 题解
[HAOI2008]硬币购物 题解
背包+容斥
Statement
[HAOI2008]硬币购物 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
Solution
根本没有往容斥方向思考 /kk
条件计数的路子无非两条,要么寻找更简洁的充要条件,要么容斥
所以首先考虑找条件,发现题目是在数有多少 ,满足
这个条件好像不是很转化的动,所以我们考虑容斥
先一遍完全背包求出没有限制时 表示凑出 的方案数
假装现在只有一种硬币有限制,其他没有限制,限制使用 个,想算凑出 的方案数
合法的不好计算,我们容斥一下,计算不合法的情况,也就是使用了 个此硬币的情况
容易想到不合法的情况数就是 ,也就是先把这 个选出来,剩下的在做完全背包的时候,不管选择了几个 ,综合起来看肯定 个。
即,在只有一个限制下,答案为
多个限制再容斥一下就可以了
复杂度
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5;
char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
int s=0,w=1; char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
return s*w;
}
int dp[N],c[5],d[5];
int T,sum,res;
signed main(){
for(int i=1;i<=4;++i)c[i]=read();
dp[0]=1;
for(int i=1;i<=4;++i)
for(int j=c[i];j<N;++j)
dp[j]+=dp[j-c[i]];
T=read();
while(T--){
for(int i=1;i<=4;++i)d[i]=read();
sum=read(),res=0;
for(int i=0;i<=15;++i){
int t=sum,cnt=0;
for(int j=1;i<=4;++i)if((i>>(j-1))&1)
t-=c[j]*(d[j]+1),cnt^=1;
if(t<0)continue;
if(!cnt)res+=dp[t];
else res-=dp[t];
}
printf("%lld\n",res);
}
return 0;
}
Solution 2
伟大的 ttd 在深刻洞悉了多重背包的本质之后,卡住 过题!
一般的多重背包是 的,即物品种类数,容量,物品个数
for i=1...n for j=1..m for k=1...d
f[k+v_i*k]+=f[k]
容易发现这样做的时候, 贡献的位置是多个点,点与点之间距离固定
所以不妨按按照 的余数分组
固定外层 ,枚举余数 ,发现这个是一个滑动窗口
这样的话,对于每一个 而言,刚好会把所有 扫一遍,所以单次询问复杂度
总复杂度 ,刚好卡过
这个其实也就是所谓单调队列优化多重背包的思路
//copied from ttd!!1
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
inline int read()
{
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
int n, s;
int c[5], d[5], dp[5][N];
signed main()
{
c[1] = read(), c[2] = read(), c[3] = read(), c[4] = read(), n = read();
while(n--){
d[1] = read(), d[2] = read(), d[3] = read(), d[4] = read(), s = read();
memset(dp, 0, sizeof(dp)), dp[0][0] = 1;
for(register int i = 1; i <= 4; i++){
for(register int j = 0; j < c[i]; j++){
int sum = 0;
for(register int k = 0; j + k * c[i] <= s; k++){
sum += dp[i - 1][j + k * c[i]];
if(k - d[i] - 1 >= 0) sum -= dp[i - 1][j + (k - d[i] - 1) * c[i]];
dp[i][j + k * c[i]] += sum;
}
}
}
printf("%lld\n", dp[4][s]);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】