2023 合肥站 热身赛 B Problem F. Flower’s Land 换根dp 依赖背包

发布时间 2023-11-27 15:14:37作者: chdy

传送门。

求出包含某个点连通块大小为K的权值和最大值。

钦定1为根节点,只求根节点的答案,其实是一个依赖性01背包问题可以\(nk\)的时间内解决。

考虑进行换根操作,由于背包是取max的背包没办法进行背包的删除,然而取前后缀背包背包的合并为\(k^2\)复杂度过高。

当时还有一个想法是点分树,但是维护的信息还是太多也需要背包的合并。

我们考虑 up and down dp。

\(f_{x,w}\)表示节点为x的子树内背包大小为\(w\)的最大价值。

\(g_{x,w}\)表示节点为x向上不包含子树背包大小为\(w\)的最大价值。

\(f\)的转移是trivial的。

考虑\(g\)数组的求出。采用前后缀的背包进行合并。

这道题很特殊由于是树上依赖背包,我们要确保复杂度正确,直接合并\(g\)数组要枚举子树外大小,要合并的子树大小,无脑合并复杂度显然不对最坏就给卡到\(nk^2\)了。

我们基于当前子树设状态\(g_{x,w}\)表示节点为x外边大小为\(k-w\)的最大价值。

这样可以减少无用状态的枚举,保证均摊复杂度为\(nk\)

//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <ctime>
#include <cctype>
#include <queue>
#include <deque>
#include <stack>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cstring>
#include <string>
#include <ctime>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <queue>
#include <deque>
#include <stack>
#include <vector>
#include <algorithm>
#include <utility>
#include <bitset>
#include <set>
#include <map>
#define ll long long
#define db double
#define INF 1000000000
#define inf 100000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define putl_(x) printf("%lld ",x);
#define get(x) x=read()
#define putl(x) printf("%lld\n",x)
#define rep(p,n,i) for(int i=p;i<=n;i+=1)
#define fep(n,p,i) for(int i=n;i>=p;--i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int>
#define mk make_pair
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define sq sqrt
#define x(w) t[w].x
#define r(w) t[w].r
#define id(w) t[w].id
#define R(w) s[w].r
#define yy p<<1|1
#define zz p<<1
#define sum(w) t[w].sum
#define mod 998244353
#define sc(A) scanf("%d",&A)
#define scs(A) scanf("%s",A);
#define put(A) printf("%d\n",A)
#define min(x,y) (x>=y?y:x)
#define max(x,y) (x>=y?x:y)
#define sub(x,y) (x-y<0?x-y+mod:x-y)
#define mes(a,k) memset(a,k,sizeof(a))
using namespace std;
const int MAXN = 40010, N = 3010;
int n, k;
int a[MAXN], sz[MAXN];
int tmp[MAXN], cnt[MAXN], ans[MAXN];
int f[MAXN][N], g[MAXN][N];
vector<int>G[MAXN];

void dfs(int x, int fa) {
	f[x][1] = a[x];
	sz[x] = 1;
	for (int cc = 0, tn; cc < G[x].size(); ++cc) {
		tn = G[x][cc];
		if (tn == fa)
			continue;
		dfs(tn, x);
		rep(0, min(sz[x], k), i)g[tn][i] = f[x][i];
		mes(tmp, 0xcf);
		rep(0, min(sz[x], k), i)rep(0, min(k - i, sz[tn]), j)
		tmp[i + j] = max(tmp[i + j], f[x][i] + f[tn][j]);
		sz[x] += sz[tn];
		rep(0,min(sz[x],k),i)f[x][i]=tmp[i];
	}
	f[x][0] = 0;
}

void dp(int x, int fa) {

	g[x][k]=0;
	rep(1, k, i)ans[x] = max(ans[x], f[x][i] + g[x][i]);
	//mes(tmp, 0xcf);
	rep(0, k, i)tmp[i] = g[x][i];
	reverse(G[x].begin(), G[x].end());
	int sum = sz[x];
	for (int cc = 0, tn; cc < G[x].size(); ++cc) {
		tn = G[x][cc];
		if (tn == fa)
			continue;
		mes(cnt, 0xcf);
		sum -= sz[tn];
		rep(0, min(sz[tn], k), i)
		rep(0, min(sum, k-i), j)
		{
			cnt[i] = max(cnt[i], g[tn][j] + tmp[i+j]);
		}
		rep(0, min(k, sz[tn]), i)g[tn][i] = cnt[i];
		mes(cnt, 0xcf);
		rep(0, min(k, sum), i)
		rep(0, min(sz[tn], k-i), j) {
			cnt[i] = max(cnt[i], f[tn][j] + tmp[i+j]);
		}
		rep(0, k, i)tmp[i] = cnt[i];
	}
	for (int cc = 0, tn; cc < G[x].size(); ++cc) {
		tn = G[x][cc];
		if (tn != fa)
			dp(tn, x);
	}
}

signed main() {
//	freopen("1.in", "r", stdin);
	sc(n);
	sc(k);
	rep(1, n, i)sc(a[i]);
	rep(2, n, i) {
		int x, y;
		sc(x);
		sc(y);
		G[x].pb(y);
		G[y].pb(x);
	}
	mes(f, 0xcf);
	dfs(1, 0);
	dp(1, 0);
	rep(1, n, i)printf("%d ", ans[i]);
	return 0;
}