📜  为 N 元有根树构建段树(1)

📅  最后修改于: 2023-12-03 14:48:54.980000             🧑  作者: Mango

以 '为 N 元有根树构建段树' 作主题

简介

在算法竞赛中,树上的问题非常常见。为了解决树上问题,我们通常需要将树转换为数组形式,以便于利用区间查询。而这时候,一个很好的选择便是使用段树。本篇文章将会介绍如何以 N 元有根树的形式构建段树。

段树简介

段树(Segment Tree)是一种用于解决区间查询的数据结构,利用树状数组或者线段树实现。它的原理是将区间分为若干个子区间,对每个子区间求出相应的信息,再将这些信息合并到整个区间来,从而实现区间查询。区间修改也是类似的操作。

如何构建 N 元有根树

在 N 元有根树中,每个节点可以有不止两个子节点,因此它比二叉树更为复杂。但是,我们可以利用类似于将二叉树转化为数组的方式来表示 N 元有根树。

我们可以按照以下方式构建 N 元有根树:

  • 将每个节点编号,从1开始,按照深度优先搜索的次序(遍历左子树 > 遍历右子树)为其编号;
  • 每个节点拥有一个唯一的编号,以及其父亲节点的编号和儿子节点的编号;
  • 对于每个节点,它的儿子节点按照编号递增的方式来记录。

构建好树结构后,我们需要在数组中指定节点的初始范围和存储的值。对于每个节点,我们可以将其子树中的编号按照顺序存入一个子数组中。例如,第一个子节点的初始范围是 $[2, 3]$,第二个子节点的初始范围是 $[4, 6]$,则该节点的子数组为 $[2, 3, 4, 5, 6]$。这样,我们就实现了将 N 元有根树转化为数组的操作。

如何利用 N 元有根树构建段树

构建好了 N 元有根树的数组表示,我们就可以类比处理二叉树的方式,构建段树了。

首先,我们需要定义一个节点结构体,来记录每个节点的属性。对于每个节点,我们需要记录其对应的区间范围、值、懒惰标记(用于记录区间修改)等信息。我们可以如下定义该结构体:

struct Node {
    int l, r;   // 当前节点的区间范围
    int sum;    // 当前节点存储的值(可为区间和等等)
    int lazy;   // 当前节点的懒惰标记(可为区间加值等等)
}tree[MAXN << 2];

接下来,我们需要实现构建段树的函数。这里介绍两种方式:

1. 递归实现

递归实现很好理解。我们首先选用根节点为 $1$ 的节点,对于该节点,我们将其子树分成两个区间,左区间为当前节点的左孩子,右区间为当前节点的右孩子。这样,我们可以使用递归来构建整个树,直到节点区间的大小为 $1$,即为叶子节点。对于每个节点,我们将其左右区间对应的两个节点的信息合并,也就是当前节点的信息就是其左右孩子信息的合并。实现代码如下:

void build(int node, int l, int r) {
    tree[node].l = l;
    tree[node].r = r;
    if (l == r) {
        // 叶子节点不需要合并信息!
        tree[node].sum = val[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(node<<1, l, mid);
    build(node<<1|1, mid+1, r);

    // 将子节点信息合并
    tree[node].sum = tree[node<<1].sum + tree[node<<1|1].sum;
}
2. 非递归实现

我们也可以使用非递归的方式来实现构建段树。非递归实现的主要思路是依次处理N元有根树的每一个节点,按照深度优先搜索的顺序,从上到下、从左到右依次遍历。在这个过程中,我们使用一个栈来模拟递归实现过程中的函数调用,从而实现构建整个树的操作。实现代码如下:

int stack[MAXN];
void build() {
    // 从根节点开始处理
    int top = 0;
    stack[++top] = 1;
    while (top) {
        // 取出当前需要处理的节点
        int u = stack[top--];
        // 如果是叶子节点,直接赋值即可
        if (tree[u].l == tree[u].r) {
            tree[u].sum = val[tree[u].l];
        } else {
            // 否则将其左右节点入栈
            int mid = (tree[u].l + tree[u].r) >> 1;
            tree[u<<1].l = tree[u].l; tree[u<<1].r = mid;
            tree[u<<1|1].l = mid+1; tree[u<<1|1].r = tree[u].r;
            stack[++top] = u<<1;
            stack[++top] = u<<1|1;
        }
        // 合并左右节点信息
        if (u << 1 < MAXN) {
            tree[u].sum = tree[u<<1].sum + tree[u<<1|1].sum;
        }
    }
}
总结

以上就是利用 N 元有根树构建段树的全部内容。将 N 元有根树转化为数组、构建节点结构体、实现递归、非递归两种方式的构建函数,就能快速构建出段树了。