题目大意
给你一棵树,让你断掉一条边,使得形成的两棵树的直径和最大。
1≤n≤1000001≤n≤100000
Solution1
看别人可以用DFSDFS序 + 线段树 + RMQRMQ求LCALCA来做,不过倍增的LCALCA会被卡,让我感到很难受,所以就没写。
Solution2
题解原话 :裸的树形DP。
照着这个思路思考了一下,发现还是很可做的,而且复杂度比上面的那个要少个loglog。
我们对每个节点now记录这几个东西 :
fst[now] :从nownow开始向下的最长链。
snd[now] :从nownow开始向下的次长链。
trd[now] :从nownow开始向下的第三长链。
s1[now] :从nownow开始向下的最长链的nownow能直接到的节点。
s2[now] :从nownow开始向下的次长链的nownow能直接到的节点。
注 : 这些可能会与想的不同,详见代码。
d[now] : 表示以nownow为根节点的树的直径。
d1[now] :表示nownow向下能直接到的节点中,以自己为根的子树最大直径。
d2[now] :表示nownow向下能直接到的节点中,以自己为根的子树次大直径。
ds[now] :表示nownow向下能直接到的节点中,以自己为根的子树最大直径的节点。
分割线上面的可以在第一次DFSDFS中求出来,下面的可以在第二次DFSDFS中求出来。
预处理之后,枚举断哪条边,然后可以O(1)O(1)得出答案。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
const int maxn = 100007;
const int maxm = 1e3 + 7;
typedef long long ll;
class Map{
private :
int n;
ll ans;
struct Node{
int u, v, w, nex;
Node (int u = 0, int v = 0, int w = 0) :
u(u),
v(v),
w(w),
nex(0) {}
};
Node edge[maxn * 2];
int head[maxn], cnt;
ll fst[maxn], snd[maxn], trd[maxn]; //链
int s1[maxn], s2[maxn];
ll d[maxn], d1[maxn], d2[maxn]; //直径
int ds[maxn];
ll fa[maxn]; //子树外的距离
public :
Map() :
cnt(1) {}
void Init(int x) {
n = x;
}
void AddEdge(int u, int v, int w) {
edge[++cnt] = Node(u, v, w);
edge[cnt].nex = head[u];
head[u] = cnt;
}
void Search(int now, int fath) {
ll sum = 0;
for (int i = head[now]; i; i = edge[i].nex) {
if (edge[i].v == fath) {
continue;
}
int v = edge[i].v, w = edge[i].w;
Search(v, now);
if (fst[v] + w > sum) {
sum = fst[v] + w;
s1[now] = v;
}
}
fst[now] = sum;
sum = 0;
for (int i = head[now]; i; i = edge[i].nex) {
if (edge[i].v == fath || edge[i].v == s1[now]) {
continue;
}
int v = edge[i].v, w = edge[i].w;
if (fst[v] + w > sum) {
sum = fst[v] + w;
s2[now] = v;
}
}
snd[now] = sum;
for (int i = head[now]; i; i = edge[i].nex) {
if (edge[i].v == fath || edge[i].v == s1[now] || edge[i].v == s2[now]) {
continue;
}
int v = edge[i].v, w = edge[i].w;
trd[now] = std :: max(trd[now], fst[v] + w);
}
}
void DFS(int now, int fath) {
d[now] = fst[now] + snd[now];
ll sum = 0;
for (int i = head[now]; i; i = edge[i].nex) {
if (edge[i].v == fath) {
continue;
}
int v = edge[i].v, w = edge[i].w;
if (v == s1[now]) {
fa[v] = std :: max(fa[now] + w, snd[now] + w);
} else {
fa[v] = std :: max(fa[now] + w, fst[now] + w);
}
DFS(v, now);
d[now] = std :: max(d[now], d[v]);
if (d[v] > sum) {
sum = d[v];
ds[now] = v;
}
}
d1[now] = sum;
for (int i = head[now]; i; i = edge[i].nex) {
if (edge[i].v == fath || edge[i].v == ds[now]) {
continue;
}
int v = edge[i].v, w = edge[i].w;
d2[now] = std :: max(d2[now], d[v]);
}
}
void Find(int now, int fath, ll &ans) {
for (int i = head[now]; i; i = edge[i].nex) {
int v = edge[i].v, w = edge[i].w;
if (v == fath) {
continue;
}
ll res = d[v];
if (v == ds[now]) {
ll sum = d2[now];
if (v == s1[now]) {
sum = std :: max(sum, snd[now] + std :: max(fa[now], trd[now]));
} else {
sum = std :: max(sum, fst[now] + std :: max(fa[now], snd[now]));
}
ans = std :: max(ans, sum + res);
} else {
ll sum = d1[now];
if (v == s1[now]) {
sum = std :: max(sum, snd[now] + std :: max(fa[now], trd[now]));
} else {
sum = std :: max(sum, fst[now] + std :: max(fa[now], snd[now]));
}
ans = std :: max(ans, sum + res);
}
Find(v, now, ans);
}
}
};
class Solution {
private :
int n;
Map mp;
ll ans;
public :
Solution () {
Get();
Solve();
}
void Get() {
freopen("etg.in", "r", stdin);
freopen("etg.out", "w", stdout);
scanf("%d", &n);
mp.Init(n);
for (int i = 1; i <= n; i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
mp.AddEdge(u, v, w);
mp.AddEdge(v, u, w);
}
}
void Solve() {
mp.Search(1, 0);
mp.DFS(1, 0);
mp.Find(1, 0, ans);
printf("%lld\n", ans);
}
};
Solution sol;
int main() {}
656



被折叠的 条评论
为什么被折叠?



