Solution
感天动地。。几百年没在场上写点分了的感觉== (然后就写了很久qwq)
不过感觉自己想这道题怎么颇有运气成分==大概是:诶是不是快乐树d一下就好了啊,哦不对看错题了,哦不对好像不能直接快乐树d啊,那不知道怎么搞的话就试试点分咯
然后就撞对了==虽然说感觉复杂度很爆炸但是莫名其妙过掉了。。只能说数据比较水qwq
首先这个题目描述有点qwq其实意思就是你最后购买东西的点必须是一个连通块
然后这个时候比较正常一点自然一点的思路应该是。。发现如果用(f[i][j])记录子树内连通块最有值这种转移方式是没有优化前途的qwq然后这个时候考虑另一种计算方法,我们考虑用(f[i][j])表示对于每一个点(i)计算经过它的花费上界为(j)的连通块的最优答案
这里有一个很关键的点就是注意到因为我们钦定了是经过(i)这个点的连通块,所以往下走的时候,如果要走到某个子树里面,沿路上的点一定都要至少买一样东西,所以这个时候我们就可以先钦定经过的点全部都先买一个,然后再跑多重背包就好了
然后这个时候你发现。。如果说要解决这个问题只要再套一个点分就ok了。。
(然而实际上。。我当时的思路是:尝试用点分搞一下这题。。那就是。。算过根的答案。。噢等等这个好像可以dp好的那就这样吧)
接下来讲一下dp的一些细节
我们用(f[i][j])表示过(i)的花费上界为(j)的连通块的最大价值是多少,然后考虑在dfs的时候转移,因为每到一个节点都要先钦定拿一个(假设当前节点是(x)),所以初始化的时候应该要赋成这个点的价值(c[x]),为了方便(后面不用判断下界什么的),我们可以直接将整个(f[x])的第二维偏移(c[x])位,这样原来的(f[x][c[x]])就变成了现在的(f[x][0]),然后在dp的时候就不用管下界的问题了
然后就是在用儿子信息更新父亲的时候,正常来说是(m^2)的,但是其实我们可以做一步转移优化:我们将当前父节点的(f),加到(f[x])里面去,也就是说初始化的时候(f[x][i]=c[x]+f[fa][i]),然后后面再正常dp,这样的话在转移的时候直接(f[fa][i]=f[x][i-c[x]])就好了(减的话是因为。。我们在(f[x])中将第二维偏移了(c[x])位)
然而这样做我们的复杂度是(O(nmlognlogd))的。。如果用单调栈优化多重背包(具体的话就是。。把第二维按照%(c[x])分组,然后就可以转化成一个求组内最大值的问题了可以用单调栈进行维护)而不是直接二进制拆分的话可以去掉那个(logd)(然而场上我发现自己不会这个。。所以就写了一个带(log)的做法。。)
这里还有一个。。对于部分数据比较有用的优化(大概吧==):注意到因为每次转移的时候是(f[fa][i]=f[x][i-c[x]]),所以我们其实并不是每一个节点的第二维枚举上限都是(m),而是一路减路上节点的代价,所以我们可以在dfs的时候传多一个值下去,这样(m)就不是满的了
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=510,M=4010;
struct xxx{
int y,nxt;
}a[N*2];
int w[N],c[N],d[N];
int h[N],sz[N],mx[N],vis[N];
int f[N][4010];
int n,m,tot,rt,rt_mx,ans,T;
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void get_sz(int fa,int x){
int u;
sz[x]=1; mx[x]=0;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||vis[u]) continue;
get_sz(x,u);
sz[x]+=sz[u];
mx[x]=max(mx[x],sz[u]);
}
}
void get_rt(int Rt,int fa,int x){
int u;
mx[x]=max(mx[x],sz[Rt]-sz[x]);
if (mx[x]<rt_mx) rt=x,rt_mx=mx[x];
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||vis[u]) continue;
get_rt(Rt,x,u);
}
}
void dfs(int fa,int x,int limit){
int u,tmp,num;
if (limit-c[x]<0) return;
tmp=d[x]-1;//take one first!!
for (int i=0;i<=limit-c[x];++i) f[x][i]=w[x]+f[fa][i];
for (int i=0;(1<<i)<=tmp;++i){
num=1<<i;
for (int j=limit-c[x];j>=c[x]*num;--j)
f[x][j]=max(f[x][j],f[x][j-(c[x]*num)]+w[x]*num);
tmp-=num;
}
if (tmp){
for (int j=limit-c[x];j>=c[x]*tmp;--j)
f[x][j]=max(f[x][j],f[x][j-(c[x]*tmp)]+w[x]*tmp);
}
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||vis[u]) continue;
dfs(x,u,limit-c[x]);//remember to take father's one!!
for (int j=c[u];j<=limit-c[x];++j)
f[x][j]=max(f[x][j],f[u][j-c[u]]);
}
}
void dp(int x){
dfs(0,x,m);
for (int i=0;i<=m-c[x];++i)
ans=max(ans,f[x][i]);
}
void solve(int x){
int u;
rt=0; rt_mx=n;
get_sz(0,x);
get_rt(x,0,x);
vis[rt]=1;
dp(rt);
for (int i=h[rt];i!=-1;i=a[i].nxt){
u=a[i].y;
if (vis[u]) continue;
solve(u);
}
}
void init(){
memset(h,-1,sizeof(h));
tot=0;
memset(f,0,sizeof(f));
memset(vis,false,sizeof(vis));
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y;
scanf("%d",&T);
for (int o=1;o<=T;++o){
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);
init();
for (int i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
ans=0;
solve(1);
printf("%d
",ans);
}
}