1 条题解
-
0
自动搬运
来自洛谷,原作者为

command_block
众水皆昂首,饮月唯我一。搬运于
2025-08-24 22:18:54,当前版本为作者最后更新于2020-03-23 14:33:31,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
鉴于写博客的时候,没有简单的例题,于是就给你谷造了个这样的水题。
题意 : 区间01背包问题
更多信息可见 一些常用的数据结构维护手法
一些背包基础知识
这题的背包是最优化01背包。设表示容量为时的最大总收益。
设背包大小为,每次加入一个物品,复杂度是的。合并两个背包,复杂度是的。
假如已有两个背包,查询合并后容量所对应的收益,我们不必真的合并这两个背包。
计算 即可。这样仅需.
注意到背包实质上是卷积就很容易(bushi)理解了。
综上,由于本题,我们要避免的合并背包才行。
猫树分治·引入
考虑某种奇怪的静态序列问题,我们并不会做。
但是,如果所有询问的区间有交集,那么我们就能通过下列算法得出答案。
选取所有询问都包含的某个位置,分别向左向右预处理某些东西。
对于询问的回答,只需要在左端点取信息,在右端点取信息,再组合即可。这要求(答案/状态)能够合并。
然后我们套用猫树分治,就能够处理更一般的情况了。
此外,将分治树存下来,往往就能够做到强制在线。
- 算法流程
考虑一堆询问区间和对应的状态区间。
我们取状态区间的中点,从分别向左右预处理某些信息。
遍历所有询问,如果跨过,则合并左右端点信息来回答。
如果在中,则下放到左儿子。
如果在中,则下放到右儿子。
继续分治。
(不难发现其实就是序列上的点/边分治,所以上猫树的题目可能可以上点/边分树)
本题题解
考虑猫树分治,我们得到左右两侧背包,只需要合并求一个值,那么就可以使用上文的方法。
这些背包需要分治并建立,需要的空间和的时间。
总复杂度,实现细节请查看代码。
#include<algorithm> #include<cstdio> #define MaxM 200500 #define MaxN 40500 using namespace std; inline int read(){ register int X=0; register char ch=0; while(ch<48||ch>57)ch=getchar(); while(ch>=48&&ch<=57)X=X*10+(ch^48),ch=getchar(); return X; } int n,m,h[MaxN],w[MaxN],f[MaxN][205]; struct Data {int l,r,t;}b[MaxM]; int p[MaxM],s[MaxM],ans[MaxM],tn; void solve(int l,int r,int tl,int tr) { if (tl>tr)return ; int mid=(l+r)>>1,tmid=tl-1; for (int j=0;j<=200;j++)f[mid][j]=0; for (int i=mid+1;i<=r;i++){ for (int j=0;j<h[i];j++)f[i][j]=f[i-1][j]; for (int j=h[i];j<=200;j++) f[i][j]=max(f[i-1][j],f[i-1][j-h[i]]+w[i]); } for (int j=h[mid];j<=200;j++)f[mid][j]=w[mid]; for (int i=mid-1;i>=l;i--){ for (int j=0;j<h[i];j++)f[i][j]=f[i+1][j]; for (int j=h[i];j<=200;j++) f[i][j]=max(f[i+1][j],f[i+1][j-h[i]]+w[i]); }tn=0; for (int i=tl,u;i<=tr;i++){ u=p[i]; if (b[u].r<=mid)p[++tmid]=u; else if (mid<b[u].l)s[++tn]=u; else { int ret=0; for (int i=0;i<=b[u].t;i++) ret=max(ret,f[b[u].l][i]+f[b[u].r][b[u].t-i]); ans[u]=ret; } }for (int i=1;i<=tn;i++)p[tmid+i]=s[i]; tr=tn+tmid; solve(l,mid,tl,tmid); solve(mid+1,r,tmid+1,tr); } int main() { n=read();m=read(); for (int i=1;i<=n;i++)h[i]=read(); for (int i=1;i<=n;i++)w[i]=read(); for (int i=1;i<=m;i++){ b[i].l=read();b[i].r=read(); b[i].t=read(); if (b[i].l==b[i].r){ if (b[i].t>=h[b[i].l])ans[i]=w[b[i].l]; }else p[++tn]=i; }solve(1,n,1,tn); for (int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }
- 1
信息
- ID
- 5273
- 时间
- 2000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者