BZOJ4890 [Tjoi2017]城市(洛谷P3761)

版权声明:蒟蒻Blog随意转载 https://blog.csdn.net/a1799342217/article/details/82117776

树的直径和中心

BZOJ题目传送门
洛谷题目传送门

这题想到了就很简单了。

n = 5000 ,时限给三秒,复杂度大概 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;
}

猜你喜欢

转载自blog.csdn.net/a1799342217/article/details/82117776