树的直径和中心
这题想到了就很简单了。
n=5000 n = 5000 ,时限给三秒,复杂度大概 n2 n 2 ,那么一维应该就是枚删的边了。
删完后原来的树分成两颗子树。那么答案就是对第一颗树的直径、第二颗树的直径、两棵树的半径加原来那条边的边权取max。
半径就是到树的中心最远的距离。而树的中心可以在找直径之后搞出来。
代码:
// luogu-judger-enable-o2
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5005
#define F inline
using namespace std;
struct edge{ int nxt,to,d; }ed[N<<1];
int n,m,k,ans,h[N],d[N],q[N],p[N];
bool f[N];
F char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
return l==r?EOF:*l++;
}
F int _read(){
int x=0; char ch=readc();
while (!isdigit(ch)) ch=readc();
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x;
}
#define add(x,y,z) ed[++k]=(edge){h[x],y,z},h[x]=k
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
F int bfs(int s,int fa){
for (int i=1;i<=n;i++) d[i]=f[i]=0;
f[fa]=1; int l=0,r=1,t=s; q[1]=s;
while (l<r){
int x=q[++l];
for (int i=h[x],v;i;i=ed[i].nxt)
if (!f[v=ed[i].to])
q[++r]=v,f[v]=true,d[v]=d[x]+ed[i].d,p[v]=x;
}
for (int i=1;i<=n;i++) if (d[t]<d[i]) t=i;
return t;
}
F int ct(int x){
int mn=1e9,dd=d[x];
for (;x;x=p[x])
if (mn<=max(dd-d[x],d[x])) break;
else mn=max(dd-d[x],d[x]);
return mn;
}
int main(){
n=_read(),ans=1e9;
for (int i=1,x,y,z;i<n;i++)
x=_read(),y=_read(),z=_read(),add(x,y,z),add(y,x,z);
for (int i=1;i<n;i++){
int u=ed[(i<<1)-1].to,v=ed[i<<1].to;
int x1=bfs(bfs(u,v),v),d1=d[x1],r1=ct(x1);
int x2=bfs(bfs(v,u),u),d2=d[x2],r2=ct(x2);
ans=min(ans,max(max(d1,d2),r1+r2+ed[i<<1].d));
}
return printf("%d\n",ans),0;
}