题目
点这里看题目。
BZOJ 上这还是权限题。
分析
不难发现,最后我们走过的点一定组成了树上的一个连通块。
如何枚举树上一个连通块?我们可以想到用点分治。由于每一次我们进行分治之后会进行子树继续分治,这就相当于将原图变成了几个连通块。我们只需要对于每次分治,将分治中心设定为 “ 必选 ” ,然后用某种方法计算这个连通块的所有连通子块的最优贡献即可。
不难发现每个商店的物品可以拿出来做多重背包。因此我们发现可以用树形依赖的多重背包计算贡献。设状态:
(f(u,j)):$u$的子树中,花$j$的代价可以获得的最大收益。
发现存在连通块每个点上都必须买东西的限制,因此我们必须在每个点上,先强制买一个东西之后,再进行多重背包的计算。这相当于总共可用的钱变少了。之后继续搜索,将子树上的答案合并上来。
由于本题对时间的限制不强,因此可以写多重背包二进制优化。妄图掩盖单调队列写挂的事实。
代码
#include <cstdio>
const int INF = 0x3f3f3f3f;
const int MAXN = 505, MAXM = 4005, MAXD = 105;
template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
}
template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
}
template<typename _T>
_T MAX( const _T a, const _T b )
{
return a > b ? a : b;
}
template<typename _T>
_T MIN( const _T a, const _T b )
{
return a < b ? a : b;
}
struct edge
{
int to, nxt;
}Graph[MAXN << 1];
int f[MAXN][MAXM];
int deq[MAXM];
int siz[MAXN], mx[MAXN];
int head[MAXN], w[MAXN], c[MAXN], d[MAXN];
int N, M, cnt, ans;
bool vis[MAXN];
void upt( int &x, const int v ) { x = MAX( x, v ); }
bool visible( const int u, const int fa ) { return u ^ fa && ! vis[u]; }
int getVal( const int u, const int x ) { return f[u][x] - ( x / c[u] ) * w[u]; }
void init()
{
ans = cnt = 0;
for( int i = 1 ; i <= N ; i ++ ) siz[i] = mx[i] = vis[i] = head[i] = 0;
for( int i = 1 ; i <= N ; i ++ )
for( int j = 0 ; j <= M ; j ++ )
f[i][j] = -INF;
}
void addEdge( const int from, const int to )
{
Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
head[from] = cnt;
}
void resize( const int u, const int fa )
{
siz[u] = 1, mx[u] = 0;
for( int i = head[u], v ; i ; i = Graph[i].nxt )
if( visible( v = Graph[i].to, fa ) )
resize( v, u ), siz[u] += siz[v], mx[u] = MAX( mx[u], siz[v] );
}
int getCen( const int u, const int fa, const int all )
{
if( MAX( mx[u], all - siz[u] ) <= all / 2 ) return u;
for( int i = head[u], v, tmp ; i ; i = Graph[i].nxt )
if( visible( v = Graph[i].to, fa ) )
if( tmp = getCen( v, u, all ) ) return tmp;
return 0;
}
void DFS( const int u, const int fa, int lim )
{
if( lim < c[u] ) return ;
lim -= c[u];
int D = d[u] - 1, C = c[u], W = w[u];
for( int i = 0 ; i <= lim ; i ++ ) f[u][i] = f[fa][i] + W;
for( int i = 0, cur ; ( 1 << i ) <= D ; i ++ )
{
cur = 1 << i;
for( int j = lim ; j >= C * cur ; j -- ) upt( f[u][j], f[u][j - C * cur] + W * cur );
D -= cur;
}
if( D ) for( int j = lim ; j >= C * D ; j -- ) upt( f[u][j], f[u][j - D * C] + W * D );
for( int i = head[u], v ; i ; i = Graph[i].nxt )
if( visible( v = Graph[i].to, fa ) )
{
DFS( v, u, lim );
for( int j = c[v] ; j <= lim ; j ++ )
upt( f[u][j], f[v][j - c[v]] );
}
}
void cal( const int u )
{
DFS( u, 0, M );
for( int i = 0 ; i <= M - c[u] ; i ++ ) upt( ans, f[u][i] );
}
void divide( const int u )
{
resize( u, 0 );
int tmp = getCen( u, 0, siz[u] );
cal( tmp ), vis[tmp] = true;
for( int i = head[tmp], v ; i ; i = Graph[i].nxt )
if( ! vis[v = Graph[i].to] )
divide( v );
vis[tmp] = false;
}
int main()
{
int T;
read( T );
while( T -- )
{
read( N ), read( M ); init();
for( int i = 1 ; i <= N ; i ++ ) read( w[i] );
for( int i = 1 ; i <= N ; i ++ ) read( c[i] );
for( int i = 1 ; i <= N ; i ++ ) read( d[i] );
for( int i = 1, a, b ; i < N ; i ++ )
read( a ), read( b ), addEdge( a, b ), addEdge( b, a );
divide( 1 );
write( ans ), putchar( '
' );
}
return 0;
}