[SDOI2013]直径
题目描述
小Q最近学习了一些图论知识。根据课本,有如下定义。树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度。如果一棵树有N个节点,可以证明其有且仅有N-1 条边。
路径:一棵树上,任意两个节点之间最多有一条简单路径。我们用 dis(a,b)表示点a和点b的路径上各边长度之和。称dis(a,b)为a、b两个节点间的距离。
直径:一棵树上,最长的路径为树的直径。树的直径可能不是唯一的。
现在小Q想知道,对于给定的一棵树,其直径的长度是多少,以及有多少条边满足所有的直径都经过该边。
输入格式
第一行包含一个整数N,表示节点数。 接下来N-1行,每行三个整数a, b, c ,表示点 a和点b之间有一条长度为c的无向边。
输出格式
共两行。第一行一个整数,表示直径的长度。第二行一个整数,表示被所有直径经过的边的数量。
样例 #1
样例输入 #1
6
3 1 1000
1 4 10
4 2 100
4 5 50
4 6 100
样例输出 #1
1110
2
提示
【样例说明】
直径共有两条,3 到2的路径和3到6的路径。这两条直径都经过边(3, 1)和边(1, 4)。
对于100%的测试数据:2<=N<=200000,所有点的编号都在1…N的范围内,边的权值<=10^9。
1、 求树的直径,套用模板,求出直径两个端点A 和 B, 直径 = len。关键是直径经过的必须边。
2、 以A为根,求出 与A的距离 = 直径len 的所有点, 假如是 vector<int> VB;
对 vector<int> VB 的所有点求最近公共祖先 lca1
3、 以B为根,求出 与B的距离 = 直径len 的所有点, 假如是 vector<int> VA;
对 vector<int> VA 的所有点求最近公共祖先 lca2
4、最后求 lca1 和 lca2 两点之间有多少边
算法竞赛进阶指南, 图论习题8
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10, M = 4e5 + 10;
int head[N], ver[M], Next[M], edge[M];
int n, m, tot, t; // t = (int)(log(n) / log(2)) + 1;
ll d1[N], d2[N];
int A, B; //树的直径的两个端点
bool vis[N];
ll maxlen;
ll d0[N];
int deep[N]; //节点的深度
int f[N][20]; //f[i][k] 表示节点i向上走 2^k 步
vector<int> VA, VB; // VB 包含的点 VB[i] 是和 点A 匹配成为 直径的
int lca1, lca2;
void add(int x, int y, int z)
{
ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
}
void bfs(int root)
{
memset(deep