[BZOJ4182]Shopping

题目

  点这里看题目。
   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( '\n' );
	}
	return 0;
}
posted @ 2020-03-27 19:31  crashed  阅读(96)  评论(0编辑  收藏  举报