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;
}