20251122 - 树的直径、重心、中心
树的基本概念
树是一种典型的非线性数据结构,是一种特殊的图,最大的特点就是没有环,所以他有许多良好的性质(比如可以 dp)。更绝的是,考的题目一般都很难!
树的直径
树的直径是指树中任意两点间的最长距离。
如何求出直径
方法零:
枚举每一个点,记录每一个点到其他点的距离,再求最大值。
时间复杂度:\(O((n + m)^2)\)
方法一:
先从任一点做一遍 dfs,求出距离最长的点,再从这个点再求一遍 dfs,求最长距离。
时间复杂度:\(O(n+m)\)
可这个算法 和Dijkstra一样,不能处理负权边,那可怎么办?SPFA 树形 dp。
方法二:
在 dfs 的过程中,求出最长路和次长路,再相加就是直径的长度。
但不知道能不能求直径的两个端点
时间复杂度:\(O(n+m)\)
树的重心
树的重心就是把一个点扣掉后,剩下的子树的大小最大值最小的点就是树的重心。
如何求出重心
方法零:
枚举每一个点,做一次 dfs 即可。
时间复杂度:\(O((n + m)^2)\)
方法一:
记录 size 数组表示子树的大小,那么其他的大小就是 \(n-size[u]\),这样子就可以求出重心了!
时间复杂度:\(O(n+m)\)
其实就是换根dp
树的中心
到其他点的距离最大值最小的点叫做树的中心。
方法一:
树形 dp 时记录一下即可。
方法二:
找到直径端点,在往上跳呀跳呀跳,在按照奇偶性搞一下就好了。
例题:
树的直径(两次dfs)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5 + 7;
const int P = 998244353;
const int inf = (1 << 30);
template<class T> void read(T &x){
x = 0;int f = 1;char ch = getchar();
while(!(ch >= '0' && ch <= '9')){if(ch == '-') f = -f;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
x *= f;
}
int n;
vector<int>edges[N];
int dist[N];
int max_(){
int id = 0,mx = 0;
for(int i = 1;i <= n;i++){
if(dist[i] > mx){
mx = dist[i];
id = i;
}
}
return id;
}
inline void dfs(int u,int from){
for(auto v : edges[u]){
if(v == from) continue;
dist[v] = dist[u] + 1;
dfs(v,u);
}
}
int main(){
read(n);
for(int i = 1;i < n;i++){
int x,y;
read(x),read(y);
edges[x].push_back(y);
edges[y].push_back(x);
}
dfs(1,1);
int id = max_();
dist[id] = 0;
dfs(id,id);
id = max_();
printf("%d\n",dist[id]);
return 0;
}
树的重心(老师怎么突然改题啊!)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5 + 7;
const int P = 998244353;
const int inf = (1 << 30);
template<class T> void read(T &x){
x = 0;int f = 1;char ch = getchar();
while(!(ch >= '0' && ch <= '9')){if(ch == '-') f = -f;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
x *= f;
}
int n,siz[N],pre[N],dep[N];
vector<int>edges[N];
inline void dfs(int u){
siz[u] = 1;
for(auto v : edges[u]){
if(pre[u] == v) continue;
pre[v] = u;
dep[v] = dep[u] + 1;
dfs(v);
siz[u] += siz[v];
}
}
int main(){
read(n);
for(int i = 1;i < n;i++){
int x,y;
read(x),read(y);
edges[x].push_back(y);
edges[y].push_back(x);
}
pre[1] = -1;
dfs(1);
int now_ans = inf,id;
for(int i = 1;i <= n;i++){
int res = 0;
for(auto v : edges[i]){
if(pre[v] == i){
res = max(res,siz[v]);
}else{
res = max(res,n - siz[i]);
}
}
if(res < now_ans){
id = i;
now_ans = res;
}
}
printf("%d ",id);
dep[id] = 0;
pre[id] = -1;
dfs(id);
printf("%d\n",accumulate(dep+1,dep+n+1,0));
return 0;
}
树的中心
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5 + 7;
const int P = 998244353;
const int inf = (1 << 30);
template<class T> void read(T &x){
x = 0;int f = 1;char ch = getchar();
while(!(ch >= '0' && ch <= '9')){if(ch == '-') f = -f;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
x *= f;
}
int n;
vector<int>edges[N];
int dist[N],fa[N];
int max_(){
int id = 0,mx = 0;
for(int i = 1;i <= n;i++){
if(dist[i] > mx){
mx = dist[i];
id = i;
}
}
return id;
}
inline void dfs(int u,int from){
for(auto v : edges[u]){
if(v == from) continue;
fa[v] = u;
dist[v] = dist[u] + 1;
dfs(v,u);
}
}
int main(){
read(n);
for(int i = 1;i < n;i++){
int x,y;
read(x),read(y);
edges[x].push_back(y);
edges[y].push_back(x);
}
dfs(1,1);
int id = max_();
dist[id] = 0;
dfs(id,id);
id = max_();
int dist_max = dist[id];
// printf("%d\n",dist_max);
if(!(dist_max & 1)){
for(int i = 1;i <= dist_max / 2;i++)
id = fa[id];
printf("%d\n",id);
}else{
for(int i = 1;i <= (dist_max - 1) / 2;i++) // 跳啊跳啊跳!
id = fa[id];
printf("%d %d\n",min(id,fa[id]),max(id,fa[id]));
}
return 0;
}

浙公网安备 33010602011771号