题解 P1399 【[NOI2013]快餐店】
贴个博客链接
首先我们要知道,如果是树的话,重心的位置一定在一半的直径里那个位置对吧。
所以如果图是一棵树,那么1/2的直径长就是答案了。
(然而这道题没有树的部分分)
看到有n条边就知道是基环树。
既然是基环树,那么我们就应该把环作为重点研究对象。
环就是解题的关键,习惯性地把基环树画成细菌
此时这个基环树的直径有两种情况 有经过基环 或 没有经过基环
没有经过基环的就是环上挂着的每一棵小子树的最大直径了。
有经过基环的怎么求呢?
考虑枚举每个位置把环断开(基环树常见套路)。
对每一个子树求一个最大深度,然后Max(两个子树最大深度加它们在环上的距离)就为直径了。
但我们有很多种断环方案,每一次都求一次复杂度肯定是要上天的。
所以想个办法优化(这里借鉴了ljh2000的方法)
先在1到tot(环长)的地方断开。
pre[]与bck[]分别表示前缀和后缀的 Max[某个子树最大深度+它的前缀(后缀)链长]
bs1[]和bs2[]分别表示前缀和后缀的 Max[某两个子树的最大深度+它们之间的距离]
所以每次从i到i+1的地方断开,答案的求法就很简单了。
For(i,1,tot-1){
Db mx1=max(bs1[i],bs2[i+1]);
Db mx2=max(mx1,pre[i]+bck[i+1]+crD[0]);
Fn=min(Fn,max(mx1,mx2));
}
DFS抠环、断环的时候比较繁琐一定要注意细节。
好了上代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define For(i,a,b) for(register int i=a;i<=b;++i)
#define Dwn(i,a,b) for(register int i=a;i>=b;--i)
#define Pn putchar('\n')
#define Re register
#define Db double
using namespace std;
const int N=1e5+5;
int head[N],nxt[N*2],v[N*2],cnt=1;
Db w[N*2],dia[N],z,dmx[N],Fn;
int n,m,x,y;
inline void read(int &v){
v=0;
char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')v=v*10+c-'0',c=getchar();
}
inline void read(Db &v){
v=0;
char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')v=v*10+c-'0',c=getchar();
}
void add(int ux,int vx,Db wx){
nxt[++cnt]=head[ux]; head[ux]=cnt; v[cnt]=vx; w[cnt]=wx;
nxt[++cnt]=head[vx]; head[vx]=cnt; v[cnt]=ux; w[cnt]=wx;
}
int tot=0,top=0,st[N],cr[N*2],vis[N],suc=0,fc[N];
Db crD[N*2],stD[N];
void dfsCir(int x,int fa){
vis[x]=1;
if(x==1)st[++top]=x,stD[top]=0;
for(Re int i=head[x];i;i=nxt[i]){
int vv=v[i]; if(vv==fa)continue;
if(!vis[vv]){
st[++top]=vv; stD[top]=w[i];
dfsCir(vv,x);
}else{
suc=1;
while(st[top]!=vv){
fc[st[top]]=1;
cr[++tot]=st[top];
crD[tot]=stD[top--];
}
fc[st[top]]=1;
cr[++tot]=st[top];
crD[tot]=w[i];
return;
}
if(suc)return;
}
top--;
}
int pos;
Db mxD;
void dfsTrD(int x,Db dix,int fa){
if(dix>mxD)mxD=dix,pos=x;
for(Re int i=head[x];i;i=nxt[i]){
int vv=v[i];
if(vv==fa||fc[vv])continue;
dfsTrD(vv,dix+w[i],x);
}
}
Db GetTrD(int x){
pos=mxD=0;
dfsTrD(x,0,0);
mxD=0;
dfsTrD(pos,0,0);
return mxD;
}
Db pre[N],bck[N],bs1[N],bs2[N],Ds=0;
void DP(){
Ds=mxD=0;
For(i,1,tot){
pre[i]=max(pre[i-1],dmx[cr[i]]+Ds);
if(i>=2)bs1[i]=max(bs1[i-1],mxD+Ds+dmx[cr[i]]);
mxD=max(mxD,dmx[cr[i]]-Ds);
Ds+=crD[i];
}
Ds=mxD=0;
crD[0]=crD[tot];
Dwn(i,tot,1){
bck[i]=max(bck[i+1],dmx[cr[i]]+Ds);
if(i<=tot-1)bs2[i]=max(bs2[i+1],mxD+Ds+dmx[cr[i]]);
mxD=max(mxD,dmx[cr[i]]-Ds);
Ds+=crD[i-1];
}
Fn=bs1[tot];
For(i,1,tot-1){
Db mx1=max(bs1[i],bs2[i+1]);
Db mx2=max(mx1,pre[i]+bck[i+1]+crD[0]);
Fn=min(Fn,max(mx1,mx2));
}
}
int main(){
//freopen("ex.in","r",stdin);
// freopen("ex.out","w",stdout);
read(n);
For(i,1,n){read(x); read(y); read(z); add(x,y,z);}
dfsCir(1,0);
For(i,1,tot){
fc[cr[i]]=0;
dia[cr[i]]=GetTrD(cr[i]);
fc[cr[i]]=1;
}
For(i,1,tot){
mxD=0;
dfsTrD(cr[i],0,0);
dmx[cr[i]]=mxD;
}
DP();
For(i,1,tot)Fn=max(Fn,dia[cr[i]]);
printf("%.1lf",Fn/2.0);
return 0;
}