Luogu_P3304 [SDOI2013]直径 树的直径

Luogu_P3304 [SDOI2013]直径

### 树的直径

题目链接
问的是树的几条直径中有几条边相交
设直径上一点到左端点的距离为\(lf[i]\)
到右端点的距离为\(rt[i]\)
到不过直径上点的最长链长度为\(fr[i]\)
明显就是找到两个端点
\(lf[x]=fr[x]\)\(rt[y]=fr[y]\)
求出两端点之间的边的个数就可以


代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=200010;
int n,ls,fs,head[maxn],tot,v[maxn],d[maxn],fr[maxn],fa[maxn],a[maxn],cnt;
int lf[maxn],rt[maxn];
struct node{
    int nxt,to,dis;
    #define nxt(x) e[x].nxt
    #define to(x) e[x].to
    #define dis(x) e[x].dis
}e[maxn<<1];
inline void add(int from,int to,int dis){
    to(++tot)=to;dis(tot)=dis;nxt(tot)=head[from];head[from]=tot;
}
void dfs(int x){
    v[x]=1;
    for(int i=head[x];i;i=nxt(i)){
        int y=to(i);
        if(v[y]) continue;
        fa[y]=x;
        d[y]=d[x]+dis(i);
        fs=(d[y]>=d[fs]) ? y : fs;
        dfs(y);
    }
    v[x]=0;
}
inline void geta(){
    while(fs!=ls){
        v[fs]=1;a[++cnt]=fs;
        fs=fa[fs];
    }
    a[++cnt]=ls;v[ls]=1;
}
inline void getfr(int x){
    v[x]=1;
    for(int i=head[x];i;i=nxt(i)){
        int y=to(i);
        if(v[y]) continue;
        getfr(y);
        fr[x]=max(fr[x],fr[y]+dis(i));
    }
}
signed main()
{
    scanf("%lld",&n);
    for(int x,y,z,i=1;i<n;i++){
        scanf("%lld%lld%lld",&x,&y,&z);add(x,y,z);add(y,x,z);
    }
    dfs(1);
    memset(d,0,sizeof(d));ls=fs;fs=0;
    dfs(ls);
    printf("%lld\n",d[fs]);int ds=d[fs];
    geta();
    for(int i=1;i<=cnt;i++){
        getfr(a[i]);
        lf[a[i]]=ds-d[a[i]];rt[a[i]]=d[a[i]];
        // cout<<a[i]<<" "<<fr[a[i]]<<" "<<lf[a[i]]<<" "<<rt[a[i]]<<endl;
    }
    int ll=0,rr=cnt;
    for(int i=1;i<=cnt;i++){
        if(fr[a[i]]==lf[a[i]]) ll=i;
        if(fr[a[i]]==rt[a[i]]){
            rr=i;break;
        }
    }
    // cout<<rr<<" "<<ll<<endl;
    printf("%lld\n",max(1ll*0,abs(rr-ll)));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ChrisKKK/p/11669574.html