题目大意
给定一棵
n
个结点的数,每个点有参数
现在要选出一个连通块使得价值最大。
Data Constraint
n≤500
题解
考虑点剖,每次选择重心就进行多重背包DP。
如果不选择就递归进子树继续考虑。
每次DP的时候是一个树型依赖DP,可以在DFS序上DP,每次选择一个点
i
就转移到
注意要按二的幂将物品分组。
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
#define N 500 + 10
#define M 4000 + 10
const int inf = 0x7FFFFFFF ;
struct Object {
int w , c , d ;
} P[N] ;
bool vis[N] ;
int Node[2*N] , Next[2*N] , Head[N] , tot ;
int W[20*N] , V[20*N] , st[N] , ed[N] ;
int DFN[N] , S[N] , R[N] , Size[N] , Maxs[N] ;
int f[N][M] , g[M] ;
int T , n , m , q , ans ;
int Root , All , Cnt , Minv ;
void link( int u , int v ) {
Node[++tot] = v ;
Next[tot] = Head[u] ;
Head[u] = tot ;
}
void DP() {
int Num = 0 ;
for (int i = 1 ; i <= Cnt ; i ++ ) {
for (int j = 0 ; j <= m ; j ++ ) f[i][j] = -inf ;
int now = S[i] ;
if ( i == 1 ) {
if ( !P[now].d ) return ;
for (int k = 1 ; k <= P[now].d ; k ++ ) f[1][k*P[now].c] = k * P[now].w ;
continue ;
}
int x = P[now].d - 1 , k = 1 ;
st[i] = Num + 1 ;
while ( x >= k ) {
++ Num ;
W[Num] = P[now].w * k ;
V[Num] = P[now].c * k ;
x -= k ;
k *= 2 ;
}
if ( x ) {
++ Num ;
W[Num] = P[now].w * x ;
V[Num] = P[now].c * x ;
}
ed[i] = Num ;
}
for (int i = 2 ; i <= Cnt ; i ++ ) {
for (int j = m ; j >= 0 ; j -- ) {
if ( j >= P[S[i]].c ) g[j] = f[i-1][j-P[S[i]].c] + P[S[i]].w ;
else g[j] = -inf ;
}
for (int k = st[i] ; k <= ed[i] ; k ++ ) {
for (int j = m ; j >= V[k] ; j -- ) g[j] = max( g[j] , g[j-V[k]] + W[k] ) ;
}
for (int j = 0 ; j <= m ; j ++ ) f[i][j] = max( f[i][j] , g[j] ) ;
for (int k = 0 ; k <= m ; k ++ ) f[R[S[i]]][k] = max( f[R[S[i]]][k] , f[i-1][k] ) ;
}
for (int i = 1 ; i <= Cnt ; i ++ )
for (int j = 0 ; j <= m ; j ++ ) ans = max( ans , f[i][j] ) ;
}
void GetSize( int x , int F ) {
Size[x] = Maxs[x] = 1 ;
for (int p = Head[x] ; p ; p = Next[p] ) {
if ( Node[p] == F || vis[Node[p]] ) continue ;
GetSize( Node[p] , x ) ;
Size[x] += Size[Node[p]] ;
if ( Size[Node[p]] > Maxs[x] ) Maxs[x] = Size[Node[p]] ;
}
}
void GetRoot( int x , int F ) {
Maxs[x] = max( Maxs[x] , Size[All] - Size[x] ) ;
if ( Maxs[x] < Minv ) Minv = Maxs[x] , Root = x ;
for (int p = Head[x] ; p ; p = Next[p] ) {
if ( Node[p] == F || vis[Node[p]] ) continue ;
GetRoot( Node[p] , x ) ;
}
}
void DFS( int x , int F ) {
DFN[x] = R[x] = ++ Cnt ;
S[Cnt] = x ;
for (int p = Head[x] ; p ; p = Next[p] ) {
if ( Node[p] == F || vis[Node[p]] ) continue ;
DFS( Node[p] , x ) ;
R[x] = R[Node[p]] ;
}
}
void DIV( int x ) {
Minv = 0x7FFFFFFF ;
Root = All = x ;
GetSize( x , 0 ) ;
GetRoot( x , 0 ) ;
Cnt = 0 ;
DFS( Root , 0 ) ;
DP() ;
vis[Root] = 1 ;
for (int p = Head[Root] ; p ; p = Next[p] ) {
if ( vis[Node[p]] ) continue ;
DIV( Node[p] ) ;
}
}
int main() {
freopen( "shopping.in" , "r" , stdin ) ;
freopen( "shopping.out" , "w" , stdout ) ;
scanf( "%d" , &T ) ;
while ( T -- ) {
tot = ans = 0 ;
memset( vis , 0 , sizeof(vis) ) ;
memset( Head , 0 , sizeof(Head) ) ;
scanf( "%d%d" , &n , &m ) ;
for (int i = 1 ; i <= n ; i ++ ) scanf( "%d" , &P[i].w ) ;
for (int i = 1 ; i <= n ; i ++ ) scanf( "%d" , &P[i].c ) ;
for (int i = 1 ; i <= n ; i ++ ) scanf( "%d" , &P[i].d ) ;
for (int i = 1 ; i < n ; i ++ ) {
int u , v ;
scanf( "%d%d" , &u , &v ) ;
link( u , v ) , link( v , u ) ;
}
DIV( 1 ) ;
printf( "%d\n" , ans ) ;
}
return 0 ;
}
以上.