题意:
- 一颗树,每个点代表一个物品,空间
c[i]
c
[
i
]
,数量
d[i]
d
[
i
]
,价值
w[i]
w
[
i
]
,现有一个空间为
m
m
的背包,选树上相互连接的物品,求最大价值
想法:
- 一眼树形背包,时间复杂度上天
- f[i][j]表示i的子树内,i必选一个,空间为j的最大价值
- 时间复杂度O(
n2m2d
n
2
m
2
d
)
- 由于选互相连接的物品,所以我们可以假定必须选某个点,然后做一次树形依赖多重背包,最后在递归到子树内继续找
- 那肯定是找树的重心啊,所以递归成点分治
- 树形依赖:由于要选某个点必须要选他的所有祖先,所以可以先做出
dfn
d
f
n
序
- 设
i=dfn[x]
i
=
d
f
n
[
x
]
- 选:
f[i+1][y]=max(f[i+1][y],f[i][y−c[x]]+w[x])
f
[
i
+
1
]
[
y
]
=
m
a
x
(
f
[
i
+
1
]
[
y
]
,
f
[
i
]
[
y
−
c
[
x
]
]
+
w
[
x
]
)
- 不选:
f[i+siz[x]+1][y]=max(f[i+siz[x]+1][y],f[i][y])
f
[
i
+
s
i
z
[
x
]
+
1
]
[
y
]
=
m
a
x
(
f
[
i
+
s
i
z
[
x
]
+
1
]
[
y
]
,
f
[
i
]
[
y
]
)
- 多重背包
- 选d个的话
- 把它分成
1+2+4+,,,,,+2n+y
1
+
2
+
4
+
,
,
,
,
,
+
2
n
+
y
的形式,当01背包做,每个物品即空间
c[i]∗2n
c
[
i
]
∗
2
n
,价格
w[i]∗2n
w
[
i
]
∗
2
n
- 好像还有一种单调队列的套路,找机会学一学
-时间复杂度(
nlognmlogd
n
l
o
g
n
m
l
o
g
d
)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define fb(i,x) for(i=last[x];i;i=next[i])
const int maxN=510,maxM=4010,maxn=-1e9;
using namespace std;
bool bz[maxN],broke[maxN];
int t,n,m,tot,i,w[maxN],c[maxN],d[maxN],f[maxN][maxM],last[maxN],
tov[maxN*2],next[maxN*2],pre[maxN],h[maxM],cnt,siz[maxN],
ans,x,y,cm[maxN],fa[maxN];
void insert(int x,int y){
tov[++tot]=y,next[tot]=last[x],last[x]=tot;
}
void dfss(int x){
int i,y;
bz[x]=true,pre[++cnt]=x,siz[x]=1;
fb(i,x){
y=tov[i];
if (!bz[y] && !broke[y]) dfss(y),siz[x]+=siz[y],fa[y]=x;
}
}
void find_ans(){
int x,i,j,xx,yy,k;
fo(i,1,cnt+1)
fo(j,0,m) f[i][j]=maxn;
f[1][0]=0;
fo(i,1,cnt){
x=pre[i];
fo(j,0,m) h[j]=maxn;
fo(j,c[x],m)
if (f[i][j-c[x]]!=maxn)h[j]=f[i][j-c[x]]+w[x];
y=d[x]-1;
fo (j,0,7){
if (y<cm[j]) break;
xx=cm[j]*w[x],yy=cm[j]*c[x];
fd(k,m,yy)
if (h[k-yy]!=maxn) h[k]=max(h[k],h[k-yy]+xx);
y-=cm[j];
}
xx=y*w[x],yy=y*c[x];
fd(j,m,yy)
if (h[j-yy]!=maxn) h[j]=max(h[j],h[j-yy]+xx);
fo(j,c[x],m) f[i+1][j]=max(f[i+1][j],h[j]);
fo(j,0,m)
f[i+siz[x]][j]=max(f[i+siz[x]][j],f[i][j]);
}
fo(i,0,m)
ans=max(ans,f[cnt+1][i]);
}
void dfs(int x){
int j,cut=0,yy,i;
bool bz1;
cnt=0,dfss(x);
fo(i,1,cnt){
yy=pre[i],bz1=true;
if (cnt-siz[yy]>cnt/2) continue;
fb(j,yy)
if (siz[tov[j]]>cnt/2 && tov[j]!=fa[yy] && !broke[tov[j]]) {bz1=false;
break;
}
if (bz1){
cut=yy;
break;
}
}
broke[cut]=true;
fo(i,1,cnt)
siz[pre[i]]=0,bz[pre[i]]=false,fa[pre[i]]=0;
cnt=0,dfss(cut);
find_ans();
fo(i,1,cnt)
siz[pre[i]]=0,bz[pre[i]]=false,fa[pre[i]]=0;
fb(i,cut){
yy=tov[i];
if (!broke[yy]) dfs(yy);
}
}
int main(){
freopen("shopping.in","r",stdin);
freopen("shopping.out","w",stdout);
cm[0]=1;
fo(i,1,7) cm[i]=cm[i-1]*2;
for (scanf("%d",&t);t;t--){
mem(last,0),mem(bz,false),mem(broke,false),tot=0;
scanf("%d%d",&n,&m);
fo(i,1,n) scanf("%d",&w[i]);
fo(i,1,n) scanf("%d",&c[i]);
fo(i,1,n) scanf("%d",&d[i]);
fo(i,1,n-1) scanf("%d%d",&x,&y),insert(x,y),insert(y,x);
ans=0;
dfs(1);
printf("%d\n",ans);
}
}