【题解】Luogu P3304 [SDOI2013] 直径
思路
首先第一问是裸的树的直径,因为没有负权边而且第二问会用到直径的两个端点,所以使用两次 DFS 求解。
接着考虑第二问。借用洛谷题解区的一张图:

当我们已经算出来一条直径后,就要刨掉那些分叉的边。当一个当前直径上的点到其子树内最深的距离(不包含已经是当前直径上的点)等于这个点到其子树内那个已知的直径端点的距离,则说明这个点的子树里有分叉。因为分叉的存在,子树内的点都不能算作必经边。
实现起来稍微有些困难。
实现
使用 LCA 求出当前整个直径上的点。因为只需要求一次,暴力跳就可以。
然后对这些点去搜索他们子树内最深的点。每个点只会被搜一次,所以时间复杂度是 \(O(n)\)。
接着处理他们到一个直径端点的距离。
最后判断他们到子树内最深点的距离是否等于到两侧直径端点的距离。维护双指针 \(L,R\),若是等于则往中间缩。最终 \(R-L\) 即是答案。
总时间复杂度 \(O(n)\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int n,ans,L,R;
int h[N],tot;
int dis[N],dep[N],av,dv;
int pu,pv,lenp,fa[N];
int path[N],mxd[N],dst[N],totp;
bool vis[N];
struct Node{
int to,nxt,w;
}e[2*N];
void Add(int u,int v,int w){
tot++;
e[tot].to=v;
e[tot].w=w;
e[tot].nxt=h[u];
h[u]=tot;
}
void dfs1(int u,int cur,int fath){
dis[u]=cur;
if(dv<dis[u]){
av=u;
dv=dis[u];
}
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].to;
if(v!=fath) dfs1(v,cur+e[i].w,u);
}
}
void dfs2(int u,int cur,int fath){
dep[u]=cur;
fa[u]=fath;
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].to;
if(v!=fath) dfs2(v,cur+1,u);
}
}
int dfs3(int u,int fath){
int res=0;
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fath||vis[v]) continue;
res=max(res,e[i].w+dfs3(v,u));
}
return res;
}
void patv(int u,int lca){
if(u==lca){
path[++totp]=u;
return ;
}
patv(fa[u],lca);
path[++totp]=u;
}
int LCA(int a,int b){
if(dep[a]<dep[b]) swap(a,b);
while(dep[a]>dep[b]) vis[a]=1,a=fa[a];
if(a==b){
vis[a]=1;
return a;
}
while(a!=b){
vis[a]=vis[b]=1;
a=fa[a];
b=fa[b];
}
vis[a]=1;
return a;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n-1;i++){
int u,v,w;
cin>>u>>v>>w;
Add(u,v,w),Add(v,u,w);
}
dfs1(1,0,0);
dv=0,pu=av;
dfs1(pu,0,0);
pv=av,lenp=dv;
cout<<lenp<<'\n';
dfs2(1,1,0);
int lca=LCA(pu,pv),tu=pu,tv=pv;
while(tu!=lca) path[++totp]=tu,tu=fa[tu];
patv(tv,lca);
for(int i=1;i<=totp;i++) mxd[path[i]]=dfs3(path[i],0);
for(int i=2;i<=totp;i++){
for(int j=h[path[i]];j;j=e[j].nxt){
int v=e[j].to;
if(v==path[i-1]) dst[path[i]]=dst[path[i-1]]+e[j].w;
}
}
L=1,R=totp;
for(int i=2;i<=totp;i++){
if(dst[path[i]]==mxd[path[i]]) L=i;
}
for(int i=totp-1;i>=1&&R>=L;i--){
if(dst[path[totp]]-dst[path[i]]==mxd[path[i]]) R=i;
}
cout<<R-L;
return 0;
}

浙公网安备 33010602011771号