在算法竞赛和实际工程中,我们经常会遇到需要在树结构上进行多次查询的问题。当原始树非常庞大(比如节点数达到10⁵级别),而每次查询只涉及少量关键点时,直接在整棵树上操作效率极低。这时,C语言虚树算法就派上了用场!
本文将带你从零开始理解虚树构建的核心思想,并用C语言实现一个完整的虚树构造过程。即使你是算法小白,也能轻松掌握这项高级技巧。
虚树(Virtual Tree)是一种压缩原树结构的技术。它保留了若干关键点(称为“关键节点”)以及它们之间的LCA(最近公共祖先),同时删除所有不影响这些关键点之间路径关系的非必要节点。这样可以大大缩小树的规模,从而提升后续算法(如动态规划、DFS等)的效率。
构建虚树通常包含以下几个核心步骤:
下面是一个完整的C语言代码示例,演示如何构建虚树。我们假设已经通过预处理获得了每个节点的深度、父节点和DFS序。
#include <stdio.h>#include <stdlib.h>#include <string.h>#define MAXN 100010#define LOG 17int n, m;int head[MAXN], to[MAXN << 1], nxt[MAXN << 1], ecnt = 1;int dep[MAXN], fa[MAXN][LOG + 1], dfn[MAXN], clk;int key[MAXN], stk[MAXN], top;int vhead[MAXN], vto[MAXN << 1], vnxt[MAXN << 1], vecnt;void add_edge(int u, int v) { to[ecnt] = v; nxt[ecnt] = head[u]; head[u] = ecnt++;}void dfs(int u, int f) { dfn[u] = ++clk; dep[u] = dep[f] + 1; fa[u][0] = f; for (int i = 1; i <= LOG; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1]; for (int i = head[u]; i; i = nxt[i]) { int v = to[i]; if (v == f) continue; dfs(v, u); }}int lca(int u, int v) { if (dep[u] < dep[v]) { int t = u; u = v; v = t; } for (int i = LOG; i >= 0; i--) if (dep[fa[u][i]] >= dep[v]) u = fa[u][i]; if (u == v) return u; for (int i = LOG; i >= 0; i--) if (fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i]; return fa[u][0];}int cmp(const void *a, const void *b) { return dfn[*(int*)a] - dfn[*(int*)b];}void build_virtual_tree(int k) { for (int i = 0; i < k; i++) key[i] = /* 输入的关键点 */; qsort(key, k, sizeof(int), cmp); int tmp[MAXN], cnt = 0; for (int i = 0; i < k; i++) tmp[cnt++] = key[i]; for (int i = 0; i < k - 1; i++) tmp[cnt++] = lca(key[i], key[i + 1]); // 去重并重新排序 qsort(tmp, cnt, sizeof(int), cmp); cnt = 1; for (int i = 1; i < cnt; i++) if (tmp[i] != tmp[i - 1]) tmp[cnt++] = tmp[i]; // 构建虚树 top = 0; vecnt = 1; memset(vhead, 0, sizeof(vhead)); stk[top++] = tmp[0]; for (int i = 1; i < cnt; i++) { int u = tmp[i], p = lca(stk[top - 1], u); while (top > 1 && dep[stk[top - 2]] >= dep[p]) { // 添加边 stk[top-2] -> stk[top-1] vto[vecnt] = stk[top - 1]; vnxt[vecnt] = vhead[stk[top - 2]]; vhead[stk[top - 2]] = vecnt++; top--; } if (stk[top - 1] != p) { vto[vecnt] = stk[top - 1]; vnxt[vecnt] = vhead[p]; vhead[p] = vecnt++; stk[top - 1] = p; } stk[top++] = u; } while (top > 1) { vto[vecnt] = stk[top - 1]; vnxt[vecnt] = vhead[stk[top - 2]]; vhead[stk[top - 2]] = vecnt++; top--; }}int main() { // 初始化原树,调用 dfs(1, 0) 预处理 // 然后根据查询调用 build_virtual_tree(k) return 0;}
1. 虚树中的边权通常不是原树中的边权,而是两点在原树中的距离,需额外计算。
2. 每次构建虚树前记得清空虚树的邻接表。
3. 关键点数量 k 通常远小于 n,因此虚树的节点数不超过 2k。
4. 在实际应用中,常配合C语言图论算法(如树形DP、最短路)一起使用。
通过本文的学习,你应该已经掌握了高效处理树上查询的利器——虚树算法。虽然初次接触可能觉得复杂,但只要理解了其“保留关键路径、压缩无关分支”的核心思想,就能灵活运用于各类树形问题中。
记住,掌握C语言虚树算法不仅能提升你的编程能力,还能在算法竞赛中助你一臂之力!快动手试试吧!
本文由主机测评网于2025-12-14发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://vpshk.cn/2025127628.html