# [BZOJ4182]Shopping (点分治+树上多重背包+单调队列优化)

### 分析

$dp[i][j]=max(dp[nex[x]][j])$(不选子树x)

$dp[i][j]=max(dp[i+1][j-k \times w[x]]+k*c[x])$(在x号节点买东西，买k个)

### 代码

//http://119.29.55.79/contest/88/problem/2
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 500
#define maxv 4000
using namespace std;
int T,n,m;
struct edge{
int from;
int to;
int next;
}E[maxn*2+5];
int esz=0;
esz++;
E[esz].from=u;
E[esz].to=v;
}

int totsz,root;
bool vis[maxn+5];
int sz[maxn+5];
int maxsz[maxn+5];
void get_root(int x,int fa){
sz[x]=1;
maxsz[x]=0;
int y=E[i].to;
if(y!=fa&&!vis[y]){
get_root(y,x);
sz[x]+=sz[y];
maxsz[x]=max(maxsz[x],sz[y]);
}
}
maxsz[x]=max(maxsz[x],totsz-sz[x]);
if(maxsz[x]<maxsz[root]) root=x;
}
int tim;
int dfn[maxn+5],hash_dfn[maxn+5];
int nex[maxn+5];
void get_dfn(int x,int fa){
dfn[x]=tim++;
hash_dfn[dfn[x]]=x;
sz[x]=1;
int y=E[i].to;
if(y!=fa&&!vis[y]){
get_dfn(y,x);
sz[x]+=sz[y];
}
}
nex[x]=tim;
}

int q[maxv+5];
int w[maxn+5],c[maxn+5],d[maxn+5];
int dp[maxn+5][maxv+5];
int ans=0;

void solve(int rt){
vis[rt]=1;
tim=0;
get_dfn(rt,0);
//一定要选根节点
//倒序循环，这样dp[0]就存储了选rt的答案
memset(dp[tim],0,sizeof(dp[tim]));
for(int i=tim-1;i>=0;i--){
int x=hash_dfn[i];
for(int j=0;j<=m;j++) dp[i][j]=dp[nex[x]][j];//不选
for(int b=0;b<c[x];b++){//选，多重背包单调队列优化
for(int a=0;a*c[x]+b<=m;a++){

int now=dp[i+1][a*c[x]+b]-a*w[x];
q[++tail]=a;
}
}
}
for(int i=1;i<=m;i++) ans=max(ans,dp[0][i]);
int y=E[i].to;
if(!vis[y]){
root=0;
maxsz[root]=sz[y];
totsz=sz[y];
get_root(y,rt);
solve(root);
}
}
}
void ini(){
esz=0;
ans=0;
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
memset(sz,0,sizeof(sz));
memset(maxsz,0,sizeof(maxsz));
memset(dfn,0,sizeof(dfn));
memset(hash_dfn,0,sizeof(hash_dfn));
memset(nex,0,sizeof(nex));
memset(q,0,sizeof(q));
}
int main(){
//	freopen("1.in","r",stdin);
int u,v;
scanf("%d",&T);
while(T--){
ini();
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
for(int i=1;i<=n;i++) scanf("%d",&d[i]);
for(int i=1;i<n;i++){
scanf("%d %d",&u,&v);