题目链接
http://codeforces.com/contest/743/problem/D
思路
要求的就是在树上找两个不相交的子树A和B,并且子树和A + B最大
首先,第一步肯定是将无根树转为有根树
然后,我们考虑先单独求对于每个节点i,以i为根节点的最大子树和
状态表示
d[u][s] : 当前节点为u,选中状态为s的最大子树和(s = 0代表当前节点不选,s = 1代表要选当前节点)
转移
1. s = 0:不选择当前节点,那么一定是从子树中选一个子树和最大的节点作为当前节点的最大子树和
即 d[u][s]=max{d[v][s]|v∈son[u]}
2. s = 1:即要选择当前节点,那么当前节点及其子树都应该被全部选择
即 d[u][s]=∑v∈son[s]d[v][1]
最后,对于儿子数大于2的节点,选出最大的两个子树求和即可
细节
判断无解的情况:即为1条链的时候无解,统计一下每个节点的儿子节点数即可
代码
#include <bits/stdc++.h>
using namespace std;
inline int in() {int x; scanf("%d", &x); return x;}
#define pr(x) {cout << #x << ' ' << x << endl;}
#define LL long long
const int maxn = 200000 + 5;
LL a[maxn], d[maxn][2];
int son[maxn], pa[maxn], n, vis[maxn][2];
vector<int> G[maxn];
LL dfs(int u, int s, int p, int sta) {
if (vis[u][s]) return d[u][s];
vis[u][s] = 1;
pa[u] = p;
if (G[u].size() == 1 && G[u][0] == p) return d[u][s] = (s == 1 ? a[u] : -1e15);
if (s == 1) {
d[u][s] = a[u];
for (auto v : G[u]) {
if (v == p) continue;
if (sta == 1) son[u]++;
d[u][s] += dfs(v, 1, u, sta);
}
} else {
d[u][s] = -1e15;
for (auto v : G[u]) {
if (v == p) continue;
d[u][s] = max(dfs(v, 1, u, sta), max(dfs(v, 0, u, sta), d[u][s]));
}
}
return d[u][s];
}
LL solve() {
LL res = -1e18;
for (int u = 1; u <= n; u++) {
if (son[u] < 2) continue;
vector<LL> tmp;
tmp.clear();
for (auto v : G[u]) {
if (v == pa[u]) continue;
tmp.push_back(max(d[v][1], d[v][0]));
}
sort(tmp.begin(), tmp.end(), greater<LL>());
LL max1 = tmp[0], max2 = tmp[1];
res = max(res, max1 + max2);
}
return res;
}
int main() {
n = in();
bool flag = false;
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
for (int i = 0; i < n - 1; i++) {
int u = in(); int v = in();
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 1, -1, 1);
for (int i = 1; i <= n; i++) {
if (son[i] >= 2) flag = true;
}
if (!flag) {
cout << "Impossible" << endl;
return 0;
}
dfs(1, 0, -1, 0);
LL res = solve();
cout << res << endl;
return 0;
}