物品购买 - dp - 树剖

本文介绍了一种在有限内存条件下,对树形结构上的离线背包查询进行优化的方法。通过轻重链划分和DFS遍历策略,实现了O(nw)的时间复杂度和O(wlgn)的空间复杂度,有效解决了多次询问链上背包问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:给你一棵树,每个点有一个物品,物品有体积和价值。
可离线的多次询问,拿一条到根的链上做背包的结果。
体积和价值以及询问的体积不超过32767,不超过5000个物品,5000次询问。
内存20MB!!
题解:考虑这么一件事情,离线后做dfs,会发现为了回溯要保留历史版本,但是一个点有多个子树的时候,最后一个子树遍历完了不需要回溯,因此就不需要保留历史版本了。
按照轻重链划分后先遍历轻边的顺序遍历,那么每次经过一条轻边才会保存一次,因此时间O(nw),空间O(wlgn)。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int W=33000,N=5010,M=5010,LOG=20;
int sz[N],son[N],ans[M],cur[W],f[LOG][W],mxw,c[N],w[N],q[N],cnt;vector<int> g[N];vector<pii> qv[M];
inline int ins(int *a,int x) { rep(i,c[x],mxw) a[i]=max(a[i],a[i-c[x]]+w[x]);return 0; }
inline int cpy(int *a,int *b,int n) { return memcpy(a,b,sizeof(int)*(n+1)),0; }
int dfs(int x)
{
	for(int i=0,y;i<(int)g[x].size();i++)
		sz[x]+=dfs(y=g[x][i]),(sz[y]>sz[son[x]]?son[x]=y:0);
	return ++sz[x];
}
int getans(int x)
{
	ins(cur,x);
	Rep(i,qv[x]) ans[qv[x][i].sec]=cur[qv[x][i].fir];
	if(son[x]&&sz[x]!=sz[son[x]]+1) cpy(f[++cnt],cur,mxw);
	for(int i=0,y;i<(int)g[x].size();i++)
		if((y=g[x][i])^son[x]) getans(y),cpy(cur,f[cnt],mxw);
	if(son[x]&&sz[x]!=sz[son[x]]+1) cpy(cur,f[cnt--],mxw);
	if(son[x]) getans(son[x]);return 0;
}
int main()
{
	int n=inn(),m=inn();
	rep(i,1,n) c[i]=inn(),w[i]=inn();
	rep(i,2,n) g[inn()].pb(i);
	rep(i,1,m) mxw=max(mxw,q[i]=inn());
	rep(i,1,m) qv[inn()].pb(mp(q[i],i));
	dfs(1),getans(1);
	rep(i,1,m) printf("%d\n",ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值