在算法竞赛和高级数据结构中,C++虚树算法是一种用于高效处理树上关键点问题的强大工具。当你面对一棵巨大原树,但只关心其中少数几个节点之间的关系时,虚树就能大幅降低时间和空间复杂度。本教程将手把手带你理解并实现虚树,即使你是编程小白也能轻松掌握!
虚树(Virtual Tree),也叫“压缩树”,是从原树中提取出若干关键点(通常称为“询问点”或“标记点”),并保留这些关键点之间在原树中的祖先-后代关系和LCA(最近公共祖先)信息后,构建出的一棵新树。
虚树的特点:

假设你有一棵有 10⁵ 个节点的树,但每次查询只涉及 10 个关键点。如果每次都遍历整棵树,时间复杂度会很高(O(n))。而通过构建虚树,我们只需在 O(k log k) 的时间内处理这 k 个关键点(k ≪ n),这就是树上问题优化的核心思想。
构建虚树的关键在于按 DFS 序排序关键点,并利用栈模拟 DFS 过程。以下是详细步骤:
下面是一个完整的 C++ 虚树构建模板,包含预处理 LCA 和虚树构建函数。这是算法竞赛技巧中的经典写法。
#include <bits/stdc++.h>using namespace std;const int N = 100010;const int LOG = 17;vector<int> g[N];int in[N], out[N], dep[N], fa[N][LOG];int timer = 0;// DFS 预处理 in, out, dep, favoid dfs(int u, int p) { in[u] = ++timer; dep[u] = dep[p] + 1; fa[u][0] = p; for (int i = 1; i < LOG; ++i) fa[u][i] = fa[fa[u][i-1]][i-1]; for (int v : g[u]) { if (v != p) dfs(v, u); } out[u] = timer;}// 判断 x 是否是 y 的祖先bool is_ancestor(int x, int y) { return in[x] <= in[y] && out[y] <= out[x];}// 求 LCAint lca(int x, int y) { if (is_ancestor(x, y)) return x; if (is_ancestor(y, x)) return y; for (int i = LOG - 1; i >= 0; --i) { if (!is_ancestor(fa[x][i], y)) x = fa[x][i]; } return fa[x][0];}// 构建虚树vector<int> build_virtual_tree(vector<int> key_nodes) { // 去重 sort(key_nodes.begin(), key_nodes.end()); key_nodes.erase(unique(key_nodes.begin(), key_nodes.end()), key_nodes.end()); // 按 DFS 序排序 sort(key_nodes.begin(), key_nodes.end(), [&](int a, int b) { return in[a] < in[b]; }); // 加入所有相邻点的 LCA vector<int> tmp = key_nodes; for (int i = 0; i < (int)tmp.size() - 1; ++i) { key_nodes.push_back(lca(tmp[i], tmp[i+1])); } // 再次去重并按 DFS 序排序 sort(key_nodes.begin(), key_nodes.end()); key_nodes.erase(unique(key_nodes.begin(), key_nodes.end()), key_nodes.end()); sort(key_nodes.begin(), key_nodes.end(), [&](int a, int b) { return in[a] < in[b]; }); // 用栈构建虚树 vector<int> stk; vector<int> virtual_tree_nodes; // 返回虚树中的节点(可选) for (int u : key_nodes) { while (!stk.empty()) { int w = stk.back(); if (is_ancestor(w, u)) { // w 是 u 的祖先,连边 w -> u // 此处可根据需要添加邻接表:virt_g[w].push_back(u); stk.push_back(u); break; } else { stk.pop_back(); } } if (stk.empty()) stk.push_back(u); } return key_nodes; // 返回虚树节点集合}虚树常用于以下类型的问题:
通过本教程,你应该已经掌握了 C++虚树算法的基本原理和实现方法。记住:虚树不是真的新建一棵树结构,而是通过保留关键点及其 LCA,在原树信息基础上进行高效计算。掌握这一树上问题优化技术,将极大提升你在算法竞赛中的解题能力。
建议多练习相关题目(如 CF613D、BZOJ3572),巩固对算法竞赛技巧的理解。祝你编程顺利!
本文由主机测评网于2025-12-19发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://vpshk.cn/20251210014.html