Analysis
这是一道类似于基环树直径的题。显然答案为直径除以2。
考虑暴力的做法,每次断一条环上边,然后找一下当前的直径,再取一个最小值即可。
优化也很简答,维护四个数组 (h1,h2,g1,g2) 分别代表到左端点(环上第一个点)前缀最大值,到右端点(也是环上第一个点(嘿嘿想不到吧,只不过饶了一圈))后缀最大值,以及前缀最大答案和后缀最大答案,不动的看一下图就懂了。
然后我们枚举断边(i ightarrow i+1),然后拿(max(h1[i]+h2[i+1],max(g1[i],g2[i+1])))来更新答案,别忘了再和不在环上的链取最大。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN = 100005;
const ll inf = 0x7f7f7f7f7f7f7f7f;
template <typename T> void read(T &x) {
T f = 1;
char ch = getchar();
for (; '0' > ch || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
for (x = 0; '0' <= ch && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
x *= f;
}
ll n;
vector<pair<ll, ll> > vec[MAXN];
bool vis[MAXN], mark[MAXN];
ll fa[MAXN];
ll stk[MAXN], top;
ll val[MAXN];
ll s, t;
ll dp[MAXN];
ll ans;
ll zsyy = inf;
void zsy(ll x) {
vis[x] = true;
stk[++top] = x;
for (ll i = 0; i < (ll)vec[x].size(); i++) {
ll y = vec[x][i].first;
if (!mark[y]) continue;
val[top] = vec[x][i].second;
if (!vis[y]) zsy(y);
}
}
void dpp(ll x) {
ll fir = 0, sec = 0;
for (ll i = 0; i < (ll)vec[x].size(); i++) {
ll y = vec[x][i].first;
if (mark[y]) continue;
if (y == fa[x]) continue;
fa[y] = x;
dpp(y);
dp[x] = max(dp[x], dp[y] + vec[x][i].second);
if (dp[y] + vec[x][i].second > fir) {
sec = fir;
fir = dp[y] + vec[x][i].second;
} else if (dp[y] > sec) {
sec = dp[y] + vec[x][i].second;
}
}
ans = max(ans, sec + fir);
}
bool flag;
void dfs(ll x) {
if (flag) return;
vis[x] = true;
for (ll i = 0; i < (ll)vec[x].size(); i++) {
ll y = vec[x][i].first;
if (y == fa[x]) continue;
if (flag) return;
if (vis[y]) {
flag = true;
s = x;
t = y;
return;
} else {
fa[y] = x;
dfs(y);
}
}
}
ll h1[MAXN], h2[MAXN], g1[MAXN], g2[MAXN];
int main() {
read(n);
for (ll i = 1; i <= n; i++) {
ll a, b, l;
read(a); read(b); read(l);
vec[a].push_back(make_pair(b, l));
vec[b].push_back(make_pair(a, l));
}
dfs(1);
while (s != t) {
mark[s] = 1;
s = fa[s];
}
mark[t] = 1;
memset(fa, 0, sizeof(fa));
memset(vis, 0, sizeof(vis));
for (ll i = 1; i <= n; i++) {
if (mark[i]) {
zsy(i);
break;
}
}
for (ll i = 1; i <= n; i++) {
if (mark[i]) {
dpp(i);
}
}
ll sum = val[1], cur = dp[stk[1]] + val[1];
for (ll i = 2; i <= top; i++) {
h1[i] = max(h1[i - 1], dp[stk[i]] + sum);
g1[i] = max(g1[i - 1], dp[stk[i]] + cur);
cur = max(cur, dp[stk[i]]) + val[i];
sum += val[i];
}
sum = val[top], cur = dp[stk[1]] + val[top];
for (ll i = top; i > 1; i--) {
h2[i] = max(h2[i + 1], dp[stk[i]] + sum);
g2[i] = max(g2[i + 1], dp[stk[i]] + cur);
cur = max(cur, dp[stk[i]]) + val[i - 1];
sum += val[i - 1];
}
for (ll i = 1; i <= top; i++) {//枚举断开 (i- -> i+1) 的边
zsyy = min(zsyy, max(ans, max(h1[i] + h2[i + 1], max(g1[i], g2[i + 1]))));
}
printf("%.1lf", (double)zsyy / 2);
return 0;
}