题目链接
思路:
现在图中保证一定只有一个环,这个基环树,也就是说去掉环上的任意一条边,它能形成一棵树,先看最长路径不在环上的情况,那么最长路径就是在环上的点为根的子树中了,这个求下树的直径即可。看在环上的,如果最长路径在环上,最长路径一定有一条环上的边没有经过, 假设
(u,v)
(
u
,
v
)
是没有经过地边, 可以将环断开成链,两倍环长度,然后考虑将
(u,v)
(
u
,
v
)
断开,那么现在经过环上的路径的最长长度就是
sum[i]+len[i]−sum[j]+len[j]
s
u
m
[
i
]
+
l
e
n
[
i
]
−
s
u
m
[
j
]
+
l
e
n
[
j
]
,
sum
s
u
m
数组是链上的一个距离前缀和,
len[i]
l
e
n
[
i
]
是以链上
i
i
这个节点为根的子树距离最远的距离,那么只需要维护
sum[i]+len[i]
s
u
m
[
i
]
+
l
e
n
[
i
]
,
−sum[i]+len[i]
−
s
u
m
[
i
]
+
l
e
n
[
i
]
的最大值就行了,可以直接用集合存,因为下次扫描断开
v
v
和下一个点的时候过期节点可以直接删除,而且能够保证取到的两个值的最大值之和(下标不同)就是不经过的最大路径长度。
#include<bits/stdc++.h>
typedef long long ll;
const ll maxn = 2e5 + 10;
const ll INF = 1e18 + 10;
using namespace std;
struct P {
ll id, val;
P(ll id = 0, ll v = 0) : id(id), val(v) {}
bool operator < (P p) const { return val > p.val || (val == p.val && id < p.id); }
};
typedef pair<ll, ll> pa;
ll n, m, T, kase = 1;
vector<pa> G[maxn];
ll vis[maxn], pre[maxn], is[maxn];
ll dis[maxn], len[maxn];
ll sum[maxn], res[maxn];
pa find_circle(ll x, ll fa) {
vis[x] = 1; pa ans(-1, -1);
for(ll i = 0; i < G[x].size(); i++) {
ll v = G[x][i].first;
if(v == fa) continue;
if(vis[v]) return pa(x, v);
ans = find_circle(v, x);
if(~ans.first) return ans;
}
return ans;
}
void find_id(ll x, ll fa, ll root, ll dis, ll &sum, ll &id) {
if(dis >= sum) { sum = dis; id = x; }
for(ll i = 0; i < G[x].size(); i++) {
ll v = G[x][i].first, w = G[x][i].second;
if(v == fa || (is[v] && v != root)) continue;
find_id(v, x, root, dis + w, sum, id);
}
}
void cir_list(pa now, vector<ll> &vec) {
ll x = now.first, y = now.second;
for(ll i = 0; i < G[x].size(); i++) if(G[x][i].first == y) dis[x] = G[x][i].second;
memset(vis, 0, sizeof vis);
queue<ll> que; que.push(x);
pre[x] = 0; vis[x] = 1;
while(!que.empty()) {
ll u = que.front(); que.pop();
if(u == y) break;
for(ll i = 0; i < G[u].size(); i++) {
ll v = G[u][i].first;
if(vis[v]) continue;
if(u == now.first && v == now.second) continue;
if(v == now.first && u == now.second) continue;
vis[v] = 1; pre[v] = u; dis[v] = G[u][i].second; que.push(v);
}
}
while(y) { vec.push_back(y); y = pre[y]; }
memset(is, 0, sizeof is);
for(ll i = 0; i < vec.size(); i++) is[vec[i]] = 1;
}
void deal_with(ll root, ll x, ll fa, ll d) {
len[root] = max(len[root], d);
for(ll i = 0; i < G[x].size(); i++) {
ll v = G[x][i].first;
if(v == fa || is[v]) continue;
deal_with(root, v, x, d + G[x][i].second);
}
}
vector<ll> lst;
set<P> st1, st2;
P q1[20], q2[20];
ll solve() {
st1.clear(); st2.clear();
memset(len, 0, sizeof len);
pa edge = find_circle(1, 0);
lst.clear(); cir_list(edge, lst);
ll ans = 0;
for(ll i = 0; i < lst.size(); i++) {
ll v = lst[i], sum = 0, id = 0;
find_id(lst[i], lst[i], lst[i], 0, sum, id);
sum = 0; find_id(id, 0, lst[i], 0, sum, id);
ans = max(ans, sum);
}
ll kst = INF;
for(ll i = 0; i < lst.size(); i++) deal_with(lst[i], lst[i], lst[i], 0);
ll sz = lst.size();
for(ll i = 0; i < lst.size() / 2; i++) swap(lst[i], lst[sz - 1 - i]);
for(ll i = 0; i < sz; i++) lst.push_back(lst[i]);
for(ll i = 0; i < lst.size(); i++) {
ll v = lst[i];
if(i) sum[i] = sum[i - 1] + dis[lst[i]];
else sum[i] = 0;
res[i] = len[v] - sum[i];
}
set<P>::iterator it1;
set<P>::iterator it2;
for(ll i = 0; i < lst.size(); i++) {
ll v = lst[i];
st1.insert(P(i, sum[i] + len[v]));
st2.insert(P(i, -sum[i] + len[v]));
if(i + 1 < sz) continue;
int cnt1 = 0, cnt2 = 0;
it1 = st1.begin();
it2 = st2.begin();
ll now = 0;
while(cnt1 < 2) { q1[cnt1++] = *it1; it1++; }
while(cnt2 < 2) { q2[cnt2++] = *it2; it2++; }
for(ll j = 0; j < cnt1; j++) {
for(ll k = 0; k < cnt2; k++) {
if(q1[j].id != q2[k].id) now = max(now, q1[j].val + q2[k].val);
}
}
kst = min(kst, now);
ll las_id = i - sz + 1;
st1.erase(P(las_id, sum[las_id] + len[lst[las_id]]));
st2.erase(P(las_id, -sum[las_id] + len[lst[las_id]]));
}
return max(ans, kst);
}
int main() {
while(scanf("%lld", &n) != EOF) {
for(ll i = 0; i < maxn; i++) G[i].clear();
for(ll i = 1; i <= n; i++) {
ll u, v, c;
scanf("%lld %lld %lld", &u, &v, &c);
G[u].push_back(pa(v, c));
G[v].push_back(pa(u, c));
}
memset(vis, 0, sizeof vis);
ll ans = solve();
printf("%.1f\n", (double)ans / 2);
}
return 0;
}