高级树算法
掌握树的直径、重心分解和高级树形DP技术
概述
高级树算法是解决复杂树问题的核心技术。本章节深入探讨树的直径算法、重心分解、虚树构造等高级技术,为处理大规模树上查询和优化问题提供强大工具。
核心内容
1. 树的直径算法
// 树的直径算法
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
class TreeDiameter {
private:
int n;
vector<vector<pair<int, long long>>> adj;
// BFS找最远节点
pair<int, long long> bfs(int start) {
vector<long long> dist(n, -1);
queue<int> q;
q.push(start);
dist[start] = 0;
int farthest = start;
long long maxDist = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
for (auto [v, weight] : adj[u]) {
if (dist[v] == -1) {
dist[v] = dist[u] + weight;
q.push(v);
if (dist[v] > maxDist) {
maxDist = dist[v];
farthest = v;
}
}
}
}
return {farthest, maxDist};
}
// DFS计算直径
pair<long long, long long> dfs(int u, int parent) {
long long max1 = 0, max2 = 0; // 最长和次长路径
for (auto [v, weight] : adj[u]) {
if (v != parent) {
auto [childMax, childDiameter] = dfs(v, u);
diameter = max(diameter, childDiameter);
long long pathLen = childMax + weight;
if (pathLen > max1) {
max2 = max1;
max1 = pathLen;
} else if (pathLen > max2) {
max2 = pathLen;
}
}
}
diameter = max(diameter, max1 + max2);
return {max1, diameter};
}
long long diameter;
public:
TreeDiameter(int vertices) : n(vertices), diameter(0) {
adj.resize(n);
}
void addEdge(int u, int v, long long weight = 1) {
adj[u].push_back({v, weight});
adj[v].push_back({u, weight});
}
// 方法1:两次BFS
long long getDiameterBFS() {
if (n == 0) return 0;
// 第一次BFS找一个端点
auto [node1, dist1] = bfs(0);
// 第二次BFS找另一个端点
auto [node2, diameter] = bfs(node1);
return diameter;
}
// 方法2:一次DFS
long long getDiameterDFS() {
if (n == 0) return 0;
diameter = 0;
dfs(0, -1);
return diameter;
}
// 获取直径路径
vector<int> getDiameterPath() {
if (n == 0) return {};
auto [node1, dist1] = bfs(0);
// 修改BFS记录路径
vector<long long> dist(n, -1);
vector<int> parent(n, -1);
queue<int> q;
q.push(node1);
dist[node1] = 0;
int node2 = node1;
long long maxDist = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
for (auto [v, weight] : adj[u]) {
if (dist[v] == -1) {
dist[v] = dist[u] + weight;
parent[v] = u;
q.push(v);
if (dist[v] > maxDist) {
maxDist = dist[v];
node2 = v;
}
}
}
}
// 重构路径
vector<int> path;
int current = node2;
while (current != -1) {
path.push_back(current);
current = parent[current];
}
return path;
}
};
// 重心分解
class CentroidDecomposition {
private:
int n;
vector<vector<int>> adj;
vector<bool> removed;
vector<int> subtreeSize;
// 计算子树大小
int calculateSize(int u, int parent) {
subtreeSize[u] = 1;
for (int v : adj[u]) {
if (v != parent && !removed[v]) {
subtreeSize[u] += calculateSize(v, u);
}
}
return subtreeSize[u];
}
// 找重心
int findCentroid(int u, int parent, int treeSize) {
for (int v : adj[u]) {
if (v != parent && !removed[v] && subtreeSize[v] > treeSize / 2) {
return findCentroid(v, u, treeSize);
}
}
return u;
}
// 分解过程
void decompose(int u, int parent) {
int treeSize = calculateSize(u, -1);
int centroid = findCentroid(u, -1, treeSize);
removed[centroid] = true;
// 处理重心
processCentroid(centroid);
// 递归分解子树
for (int v : adj[centroid]) {
if (!removed[v]) {
decompose(v, centroid);
}
}
}
// 处理重心的逻辑(可自定义)
void processCentroid(int centroid) {
cout << "处理重心: " << centroid << endl;
// 这里可以添加具体的处理逻辑
// 例如:计算经过重心的路径
}
public:
CentroidDecomposition(int vertices) : n(vertices) {
adj.resize(n);
removed.resize(n);
subtreeSize.resize(n);
}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void decompose() {
fill(removed.begin(), removed.end(), false);
decompose(0, -1);
}
};
// 虚树构造
class VirtualTree {
private:
int n, timer;
vector<vector<int>> adj;
vector<int> depth, parent, tin, tout;
vector<vector<int>> up; // 二进制提升
void dfs(int u, int p, int d) {
tin[u] = timer++;
depth[u] = d;
parent[u] = p;
up[u][0] = p;
for (int i = 1; i < up[u].size(); i++) {
if (up[u][i-1] != -1) {
up[u][i] = up[up[u][i-1]][i-1];
}
}
for (int v : adj[u]) {
if (v != p) {
dfs(v, u, d + 1);
}
}
tout[u] = timer++;
}
bool isAncestor(int u, int v) {
return tin[u] <= tin[v] && tout[u] >= tout[v];
}
int lca(int u, int v) {
if (isAncestor(u, v)) return u;
if (isAncestor(v, u)) return v;
for (int i = up[u].size() - 1; i >= 0; i--) {
if (up[u][i] != -1 && !isAncestor(up[u][i], v)) {
u = up[u][i];
}
}
return up[u][0];
}
public:
VirtualTree(int vertices) : n(vertices), timer(0) {
adj.resize(n);
depth.resize(n);
parent.resize(n);
tin.resize(n);
tout.resize(n);
int logN = 0;
while ((1 << logN) <= n) logN++;
up.assign(n, vector<int>(logN, -1));
}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void preprocess(int root = 0) {
timer = 0;
dfs(root, -1, 0);
}
// 构造虚树
vector<vector<int>> buildVirtualTree(vector<int>& important) {
if (important.empty()) return {};
// 按DFS序排序
sort(important.begin(), important.end(), [&](int a, int b) {
return tin[a] < tin[b];
});
// 添加LCA节点
vector<int> nodes = important;
for (int i = 0; i < important.size() - 1; i++) {
int l = lca(important[i], important[i + 1]);
nodes.push_back(l);
}
// 去重并排序
sort(nodes.begin(), nodes.end());
nodes.erase(unique(nodes.begin(), nodes.end()), nodes.end());
sort(nodes.begin(), nodes.end(), [&](int a, int b) {
return tin[a] < tin[b];
});
// 构造虚树
vector<vector<int>> vtree(n);
vector<int> stack;
for (int u : nodes) {
while (!stack.empty() && !isAncestor(stack.back(), u)) {
stack.pop_back();
}
if (!stack.empty()) {
vtree[stack.back()].push_back(u);
}
stack.push_back(u);
}
return vtree;
}
};
int main() {
// 测试树的直径
TreeDiameter td(6);
td.addEdge(0, 1, 2);
td.addEdge(1, 2, 3);
td.addEdge(1, 3, 1);
td.addEdge(3, 4, 4);
td.addEdge(3, 5, 2);
cout << "BFS方法直径: " << td.getDiameterBFS() << endl;
cout << "DFS方法直径: " << td.getDiameterDFS() << endl;
vector<int> path = td.getDiameterPath();
cout << "直径路径: ";
for (int i = 0; i < path.size(); i++) {
cout << path[i];
if (i < path.size() - 1) cout << " -> ";
}
cout << endl;
// 测试重心分解
CentroidDecomposition cd(7);
cd.addEdge(0, 1);
cd.addEdge(1, 2);
cd.addEdge(1, 3);
cd.addEdge(3, 4);
cd.addEdge(3, 5);
cd.addEdge(5, 6);
cout << "重心分解:" << endl;
cd.decompose();
// 测试虚树
VirtualTree vt(8);
vt.addEdge(0, 1);
vt.addEdge(0, 2);
vt.addEdge(1, 3);
vt.addEdge(1, 4);
vt.addEdge(2, 5);
vt.addEdge(2, 6);
vt.addEdge(6, 7);
vt.preprocess(0);
vector<int> important = {3, 4, 7};
vector<vector<int>> vtree = vt.buildVirtualTree(important);
cout << "虚树构造完成" << endl;
return 0;
}解题技巧
🎯 算法选择
树的直径用两次BFS或一次DFS,重心分解处理路径问题,虚树优化特定节点查询。
⚡ 复杂度分析
直径算法O(n),重心分解O(n log n),虚树构造O(k log k),k为重要节点数。
🔧 实现要点
重心分解要正确维护removed数组,虚树构造需要LCA预处理和DFS序排序。
🧮 应用场景
树上距离查询、路径统计、子树操作优化等高级树问题的高效解决方案。