题目大意:给你一棵树,每个点有一个物品,物品有体积和价值。
可离线的多次询问,拿一条到根的链上做背包的结果。
体积和价值以及询问的体积不超过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;
}