题目
https://www.luogu.com.cn/problem/P1399
思路
对于基环树,只需要断开换上的一条边然后求树的直径就可以了,可以证明断开一条边后对答案没有影响,因为距离肯定不会成一个环。
但是这复杂度不对
可以先对换上的每个外向树做一次求直径(最后的答案肯定大于等于它的一半的),求出f[i]表示以第i个点为起点,做大能在子树中延伸的长度。
然后对于换上i,j两点,他们的距离就是f[i]+f[j]+dis[i][j],dis[i][j]表示的是在环上i,j的直接距离。
拆换成链后就变成f[i]+f[j]+sum[j] - sum[i], sum[i]表示拆环成链后边权的前缀和,然后f[i]-sum[i]和f[j]-sum[j]直接开两个堆维护一下最大值就可以了,注意要考虑i == j的情况。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+77;
struct E
{
int to,val;
};
vector<E> g[N];
int n,loop[N],sign,low[N],dfn[N],st[N],top,onloop[N],L,R;
ll dis[N],fl[N],pre[N],premx[N],
ans = 1e15,mx = 0,lst[N],pans[N],sans[N],sumlst[N],oo = 0,sum = 0,summ = 0,
o = 0,mxd = 0;
void Tarjan(int x,int fa) {
dfn[x] = low[x] = ++sign,st[++top] = x;
for(E i : g[x]) {
int y = i.to;
if(y == fa)
continue;
if(dfn[y])
low[x] = min(low[x],dfn[y]);
else
Tarjan(y,x),low[x] = min(low[x],low[y]);
}
if(low[x] == dfn[x]) {
int tmp;
if(st[top] == x)
top--;
else
do {
tmp = st[top--],loop[++loop[0]] = tmp,onloop[tmp] = 1;
} while (tmp != x);
}
}
void dfs(int x,int fa,int &p,ll dis,int f) {
if(dis >= mxd)
mxd = dis,p = x;
for(E i : g[x])
if(i.to != fa && (!onloop[i.to] || i.to == f))
dfs(i.to,x,p,dis + i.val,f);
}
int main() {
cin >> n;
for(int i = 1,x,y,z; i <= n; i++)
cin >> x >> y >> z,g[x].push_back({ y,z }),g[y].push_back({ x,z });
Tarjan(1,0);
for(int i = 1; i <= loop[0]; i++) {
mxd = 0,dfs(loop[i],0,L,0,loop[i]),dis[i] = mxd;
dfs(L,0,R,0,loop[i]),oo = max(oo,mxd);
}
premx[1] = dis[loop[1]];
for(E j : g[loop[1]])
if(j.to == loop[loop[0]])
lst[1] = j.val;
for(int i = 2; i <= loop[0]; i++) {
for(E j : g[loop[i]])
if(j.to == loop[i - 1])
lst[i] = j.val;
sum += lst[i],premx[i] = max(sum + dis[i],premx[i - 1]);
}
mx = 0;
for(int i = 1; i <= loop[0]; i++) {
if(i != 1)
summ += lst[i];
pans[i] = max(pans[i - 1],dis[i] + summ + mx);
mx = max(mx,dis[i] - summ);
}
mx = 0;
for(int i = 1; i <= loop[0]; i++) sumlst[i] = sumlst[i - 1] + (i != 1 ? lst[i] : 0);
for(int i = loop[0]; i >= 1; i--) {
sans[i] = max(sans[i + 1],mx + dis[i] - sumlst[i]);
mx = max(mx,dis[i] + sumlst[i]);
}
mx = 0;
sans[loop[0] + 1] = pans[1] = 1e15;
for(int i = loop[0]; i >= 1; i--) {
ans = min(ans,max(mx + premx[i] + lst[1],max(pans[i],sans[i + 1])));
mx = max(mx,sumlst[loop[0]] - sumlst[i] + dis[i]);
}
mx = -1e15;
for(int i = 1; i <= loop[0]; i++) {
o = max(o,dis[i] + sumlst[i] + mx);
mx = max(mx,dis[i] - sumlst[i]);
}
printf("%.1lf\n",max(min(ans,o),oo) / 2.0);
}