求直径的必经边,感觉难度其实还挺大的。
#include<iostream>
#include<cstring>
using namespace std;
const int N = 2e5+5;
struct Edge{
int to, nxt, w;
}edge[N<<1];
int h[N], cnt = 1;
void add(int u, int v, int w)
{
edge[cnt] = {v, h[u], w};
h[u] = cnt++;
}
int n;
long long d[N];
int path[N], dpth[N];
int t;
void dfs(int u, int fa, bool op)
{
dpth[u] = dpth[fa] + 1;
for(int i=h[u];i;i=edge[i].nxt)
{
int j = edge[i].to;
int w = edge[i].w;
if(j==fa)
continue;
d[j] = d[u] + w;
if(d[j]>d[t])
t = j;
if(op)
path[j] = u;
dfs(j, u, op);
}
}
int l, r;
bool vis[N];
int dis[N];
//想象一下将树按照直径横放,然后从右向左扫描过来,过程中维护信息
void dfss(int u, int fa)
{
for(int i=h[u];i;i=edge[i].nxt)
{
int j = edge[i].to;
if(j==fa)
continue;
dfss(j, u);
dis[u] = max(dis[u], dis[j]+edge[i].w);
if(!vis[u] || vis[j]) //要保证当前边的起点在直径上,且终点不在直径上
continue;
//如果从当前点 u出发另有一个最长点,则当前点作为右侧的分叉点
if(dis[j]+edge[i].w==d[t]-d[u] && dpth[r]>dpth[u]) //dpth[r]>dpth[u]保证选到的 r一定是最靠左的分叉点
r = u;
//同样,维护左侧分叉点
if(dis[j]+edge[i].w==d[u] && dpth[l]<dpth[u])
l = u;
}
}
int main()
{
cin>>n;
for(int i=1;i<n;i++)
{
int u, v, w;
cin>>u>>v>>w;
add(u, v, w);
add(v, u, w);
}
dfs(1, 0, 0);
l = t;
memset(d, 0, sizeof d);
dfs(t, 0, 1);
cout<<d[t]<<endl;
r = t;
for(int i=t;i;i=path[i])
vis[i] = 1;
dfss(t, 0);
cout<<dpth[r]-dpth[l];
return 0;
}
//6
//3 1 1000
//1 4 10
//4 2 100
//4 5 50
//4 6 100