Task
有n个点,n条边,任意两点相互连通。在任意边的任意位置可设置A点,求A到n个点的距离中的最大值的最小值。
N<=1e5,边长l<=1e9
Solution
最后的答案一定在某一条边上,可以终态枚举。到每个点的距离是最短路径的问题,可以用dijksra来处理。但是怎么确定应该放在这条边的哪个位置呢?
边可以分类为链边,环边,如果在环边上dijkstra会怎么样呢?
一定会从源点从两端往中间更新。点A在一段线段中,假设左边长为x,那么右边长为d-x。
用这两个端点去dijkstra,一定会存在一个分界点,由图中不同颜色的箭头去更新。
也许可以在一条边上三分一个位置,但是30min+,无果。
如果在树上,确定一个点到每个点的距离的最大值最小,那么这个点一定是树的中心,以前树网的核中有做过,而且这个核在任意一条直径的中间。
怎么把一个基环外向树变成一棵普通的树呢?
——删去一条边。这条边就是dijkstra中一定更新不到的无用边。
因此基本的思路就是枚举断开环上的一条边,求树的直径。复杂度是O(N^2),超时了。
可以用线段树优化。
可以把环扩展2倍成链,把环上每一个点对应小树的最大距离压缩在这个点上。
最后答案为取一段长度为cirn的区间,使这个区间的直径最小。
Dis=sum[y]-sum[x-1]+mx[x]+mx[y],y>x
Sum[y]-sum[x-1]代表x到y的距离,再加上x,y各自小树对应的最长的距离,就是这个区间的直径。
可以n^2枚举这个区间的两个端点。但是也可以分治,如果把这个区间分成做区间和右区间,
那么左右端点只有可能
① 都在左区间
② 都在右区间
③ 一个在左区间,一个在右区间。
类似最优贸易简化版那道题。
最后别忘了,这个区间的直径也可能是小树的直径。
const int M=2e5+5;
ll mx[M<<1],sum[M<<1],val[M];
bool mark[M];
int id[M<<1],head[M],cnt[M],d[M];//100w
/*
链上的点代表一棵树,mx记录直径
val 这个点对应的小树内的直径
d 链上相邻两点间的距离
*/
int n,ecnt,cirn,S,X;
struct edge{
int t,v,nxt;
}e[M<<1];
inline void addedge(int f,int t,int v){
e[++ecnt]=(edge){t,v,head[f]};
head[f]=ecnt;
}
inline void input(){//建边
int i,j,k,a,b,c;
rd(n);
rep(i,1,n){
rd(a);rd(b);rd(c);
addedge(a,b,c);
addedge(b,a,c);
cnt[a]++;cnt[b]++;
}
}
inline void Topo(){//找到不在环上的点
int i,j,k,x;
queue<int>Q;
while(!Q.empty())Q.pop();
rep(i,1,n)if(cnt[i]==1)Q.push(i);//叶子节点
while(!Q.empty()){
x=Q.front();Q.pop();
mark[x]=1;
for(i=head[x];i;i=e[i].nxt)
if(--cnt[e[i].t]==1)Q.push(e[i].t);
}
}
inline void findmx(int f,int x,ll d){
if(d>mx[S]){X=x;mx[S]=d;}
for(int i=head[x];i;i=e[i].nxt)
if(e[i].t!=f&&mark[e[i].t])findmx(x,e[i].t,e[i].v+d);
}
inline void findcircle(int f,int x,int dis){
d[cirn]=dis;
if(x==S&&f)return;
id[++cirn]=x;
for(int i=head[x];i;i=e[i].nxt){
int t=e[i].t;
if(t==f||mark[t])continue;
findcircle(x,t,e[i].v);
break;
}
}
inline void findval(int f,int x,ll d){
if(d>val[S])val[S]=d;
for(int i=head[x];i;i=e[i].nxt){
int t=e[i].t;
if(t==f||!mark[t]&&t!=id[S])continue;
findval(x,t,d+e[i].v);
}
}
inline void Circle(){
int i,j,k,x,t,f;
rep(i,1,n)if(!mark[i]){t=i;break;}//环上的点
S=x=t;
findcircle(0,x,0);
rep(i,1,cirn){
S=i;
findmx(0,id[i],0);
findval(0,X,0);
}
rep(i,cirn+1,cirn<<1){
id[i]=id[i-cirn];
mx[i]=mx[i-cirn];
d[i]=d[i-cirn];
val[i]=val[i-cirn];
}
rep(i,1,cirn<<1)sum[i]=sum[i-1]+d[i-1];
}
struct Tree{
ll mi,mx,v;
inline void clear(){
mi=1e18;
mx=-1;
v=0;
}
}ANS;
struct Segment_Tree{
Tree t[M<<4];//480w
inline Tree up(Tree &L,Tree &R){
Tree C;
C.v=max(R.mx-L.mi,max(L.v,R.v));
C.mx=max(L.mx,R.mx);
C.mi=min(L.mi,R.mi);
return C;
}
inline void build(int l,int r,int p){
if(l==r){
t[p].mi=sum[l]-mx[l];
t[p].mx=sum[l]+mx[l];
t[p].v=val[l];
return;
}
int mid=l+r>>1;
build(l,mid,lsn(p));
build(mid+1,r,rsn(p));
t[p]=up(t[lsn(p)],t[rsn(p)]);
}
inline void query(int l,int r,int L,int R,int p){
if(l==L&&r==R){
ANS=up(ANS,t[p]);
return;
}
int mid=L+R>>1;
if(r<=mid)query(l,r,L,mid,lsn(p));
else if(l>mid)query(l,r,mid+1,R,rsn(p));
else query(l,mid,L,mid,lsn(p)),query(mid+1,r,mid+1,R,rsn(p));
}
}T;
inline void solve(){
int i,j,k;
ll ans=-1;
ANS.clear();
rep(i,1,cirn){
ANS.clear();
T.query(i,i+cirn-1,1,cirn<<1,1);
// printf(" %d %d %lld\n",i,i+cirn-1,ANS.v);
MIN(ans,ANS.v);
}
db res=1.0*ans/2;
printf("%.1lf\n",res);
}
int main(){
/* 16.03 Today is a happy day ll
基环外向树
*/
// freopen("foodshop0.in","r",stdin);
input();
Topo();
Circle();
T.build(1,cirn<<1,1);
solve();
return 0;
}