CF1901E Compressed Tree 题解

发布时间 2023-12-19 11:43:40作者: Creeper_l

原题链接:CF1901E,树形 dp + 神奇分类讨论。

很容易想到树形 dp。难点在于如何转移以及统计答案,需要大量分讨。

  • 父亲(及其以上)和自己组成连通块,不缩。(只保留自己并且往上传递)

  • 连通块中只有自己一个(记录答案)

  • 一个儿子和自己组成连通块,且自己作为根节点,不和父亲收缩(记录答案)

  • 一个儿子和自己组成连通块,且自己要和父亲收缩(往上转移)

  • 两个儿子和自己组成连通块,且和自己的两个儿子收缩,不和父亲收缩(记录答案)

  • 两个及以上儿子和自己组成连通块,不缩,可能还会加上父亲(往上转移)

  • 三个及以上儿子和自己,不缩,不加父亲(直接记录答案)

需要慢慢理解,具体详见代码。

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace Fread {
const int SIZE = 1 << 21;
char buf[SIZE], *S, *T;
inline char getchar() {
    if (S == T) {
        T = (S = buf) + fread(buf, 1, SIZE, stdin);
        if (S == T) return '\n';
    }
    return *S++;
}
}
namespace Fwrite {
const int SIZE = 1 << 21;
char buf[SIZE], *S = buf, *T = buf + SIZE;
inline void flush() {
    fwrite(buf, 1, S - buf, stdout);
    S = buf;
}
inline void putchar(char c) {
    *S++ = c;
    if (S == T) flush();
}
struct NTR {
    ~ NTR() { flush(); }
} ztr;
}
#ifdef ONLINE_JUDGE
#define getchar Fread :: getchar
#define putchar Fwrite :: putchar
#endif
namespace Fastio {
struct Reader {
    template<typename T>
    Reader& operator >> (T& x) {
        char c = getchar();
        T f = 1;
        while (c < '0' || c > '9') {
            if (c == '-') f = -1;
            c = getchar();
        }
        x = 0;
        while (c >= '0' && c <= '9') {
            x = x * 10 + (c - '0');
            c = getchar();
        }
        x *= f;
        return *this;
    }
    Reader& operator >> (char& c) {
        c = getchar();
        while (c == ' ' || c == '\n') c = getchar();
        return *this;
    }
    Reader& operator >> (char* str) {
        int len = 0;
        char c = getchar();
        while (c == ' ' || c == '\n') c = getchar();
        while (c != ' ' && c != '\n' && c != '\r') { // \r\n in windows
            str[len++] = c;
            c = getchar();
        }
        str[len] = '\0';
        return *this;
    }
    Reader(){}
} cin;
const char endl = '\n';
struct Writer {
    template<typename T>
    Writer& operator << (T x) {
        if (x == 0) { putchar('0'); return *this; }
        if (x < 0) { putchar('-'); x = -x; }
        static int sta[45];
        int top = 0;
        while (x) { sta[++top] = x % 10; x /= 10; }
        while (top) { putchar(sta[top] + '0'); --top; }
        return *this;
    }
    Writer& operator << (char c) {
        putchar(c);
        return *this;
    }
    Writer& operator << (char* str) {
        int cur = 0;
        while (str[cur]) putchar(str[cur++]);
        return *this;
    }
    Writer& operator << (const char* str) {
        int cur = 0;
        while (str[cur]) putchar(str[cur++]);
        return *this;
    }
    Writer(){}
} cout;
}
#define cin Fastio :: cin
#define cout Fastio :: cout
#define endl Fastio :: endl
const int MAXN = 5e5 + 10;
int n,a[MAXN],head[MAXN],cnt,x,y,T,dp[MAXN],ans;
struct Node{int u,v,nxt;}e[MAXN << 1];
inline void Add(int u,int v){e[++cnt] = {u,v,head[u]};head[u] = cnt;}
inline void init()
{
	for(int i = 1;i <= n;i++) head[i] = -1,a[i] = dp[i] = 0;
	cnt = 0,ans = 0;
}
inline void dfs(int u,int father,int root)
{
	dp[u] = a[u];//父亲(及其以上)和自己组成连通块,不缩。(只保留自己并且往上传递) 
	ans = max(ans,dp[u]);//连通块中只有自己一个(记录答案) 
	vector <int> f;
	for(int i = head[u]; ~ i;i = e[i].nxt)
	{	
		int now = e[i].v;
		if(now == father) continue;
		dfs(now,u,root),f.emplace_back(dp[now]);
		ans = max(ans,dp[now] + a[u]);// 一个儿子和自己组成连通块,且自己作为根节点,不和父亲收缩(记录答案)
		dp[u] = max(dp[u],dp[now]);//一个儿子和自己组成连通块,且自己要和父亲收缩(往上转移) 
 	}
 	sort(f.begin(),f.end(),greater<int>());
 	if(f.size() >= 2)
 	{
 		int tmp = f[0] + f[1];
		ans = max(ans,tmp);//两个儿子和自己组成连通块,且和自己的两个儿子收缩,不和父亲收缩(记录答案)
 		for(int j = 2;j < f.size() && f[j] > 0;j++) tmp += f[j];
 		dp[u] = max(a[u] + tmp,dp[u]);//两个及以上儿子和自己组成连通块,不缩,可能还会加上父亲(往上转移) 
	}
	if(f.size() >= 3)
	{
		int tmp = f[0] + f[1] + f[2];
		for(int j = 3;j < f.size() && f[j] > 0;j++) tmp += f[j];
		ans = max(ans,a[u] + tmp);//三个及以上儿子和自己组成连通块,不缩,不加父亲(直接记录答案) 
	}
}
signed main() 
{
	cin >> T;
	while(T--)
	{
		cin >> n;init();
		for(int i = 1;i <= n;i++) cin >> a[i];
		for(int i = 1;i < n;i++) cin >> x >> y,Add(x,y),Add(y,x);
		dfs(1,0,1);
		cout << ans << endl;
	}
	return 0;
}