P3304

2024-07-02

$\text{P3304 (luogu)}$

定理:树的各条直径有公共部分

那就找到一条直径(两遍 $\text{dfs}$ ),找到两个端点 $\text{L, R}$,标记一下每个点是否在直径上($\text{tag}$ 数组),然后找公共部分的两端 $\text{L1, R1}$。

找 $\text{L1, R1}$:从左往右遍历直径上的点作为 $\text{L1}$,以 $\text{L1}$ 为根 $\text{dfs}$($\text{tag}$ 过的点不搜),如果搜到的最长链长度 $\text{dp1[L1]}$ 等于 $\text{dis(L1, R)}$ 就退出,此时 $\text{L1}$ 右边的直径上的边都不可能成为公共部分,因为在 $\text{L1}$ 处出现了分叉。$\text{R1}$ 同理。

(其实这里交换 $\text{L1, R1}$ 好一点)

#include <bits/stdc++.h>
#define int long long
#define N 200005
using namespace std;
struct node { int v, w; };
vector<node> g[N];
int dp[N], dp1[N], n, pt;
int dep[N], fa[N][20];
int L, R, len, L1, R1;
int tag[N], nxt[N];
void dfs1(int x, int f) {
	dep[x] = dep[fa[x][0] = f]+1;
	for(int i = 1; i < 20; i++) fa[x][i] = fa[fa[x][i-1]][i-1];
	for(auto i : g[x]) {
		int v = i.v, w = i.w;
		if(v == f) continue;
		dp[v] = dp[x]+w;
		if(dp[v] > dp[pt]) pt = v;
		dfs1(v, x);
	}
	return;
}
int Lca(int x, int y) {
	if(dep[x] < dep[y]) swap(x, y);
	for(int i = 19; i >= 0; i--) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
	if(x == y) return x;
	for(int i = 19; i >= 0; i--) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}
int dis(int x, int y) { return dp[x]+dp[y]-2*dp[Lca(x, y)]; }
void dfs2(int x, int f) {
	for(auto i : g[x]) {
		int v = i.v, w = i.w;
		if(v == f || tag[v]) continue;
		dp1[v] = dp1[x]+w;
		if(dp1[v] > dp1[pt]) pt = v;
		dfs2(v, x);
	}
	return;
}
void dfs3(int x, int f) {
	dep[x] = dep[fa[x][0] = f]+1;
	for(int i = 1; i < 20; i++) fa[x][i] = fa[fa[x][i-1]][i-1];
	for(auto i : g[x]) {
		int v = i.v, w = i.w;
		if(v == f) continue;
		dp[v] = dp[x]+1;
		dfs3(v, x);
	}
	return;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cin >> n;
	for(int i = 1, u, v, w; i < n; i++) {
		cin >> u >> v >> w;
		g[u].push_back({v, w});
		g[v].push_back({u, w});
	}
	dp[1] = 0, dfs1(1, 0), L = pt;
	dp[pt] = 0, dfs1(pt, 0), R = pt, len = dp[pt];
	int tmp = R;
	while(tmp != L) tag[tmp] = 1, nxt[fa[tmp][0]] = tmp, tmp = fa[tmp][0];
	L1 = L;
	while(L1 != R) {
		dp1[L1] = 0, pt = L1, dfs2(L1, 0);
		if(dp1[pt] == dis(L1, R)) break;
		L1 = nxt[L1];
	}
	R1 = R;
	while(R1 != L) {
		dp1[R1] = 0, pt = R1, dfs2(R1, 0);
		if(dp1[pt] == dis(L, R1)) break;
		R1 = fa[R1][0];
	}
	dp[1] = 0, dfs3(1, 0);
	cout << len << '\n' << dis(L1, R1) << '\n';
	return 0;
}