What is a segment tree? Talking about the line segment tree

Segment tree

~

Any questions are welcome to discuss in the comment area~

Problem introduction

n numbers, m queries.
Query method: l,r
is the sum/maximum/minimum value of the interval [l,r]

Use line tree

In order to solve this problem efficiently, we need to use line segment trees.
Insert picture description here
As shown in the figure, the so-called line segment tree is to divide a large section into two small cells, and the cell is divided into two small cells until there is only one element left in the section.
There are two kinds of line segment trees, one is more diligent (hen) and the other is more lazy, with little difference in code, so just be lazy~~

Required variables

int Sum[N << 2], Add[N << 2];
// Sum: 线段树数组
// Add: lazy标记(懒懒的线段树)
int A[N], n;
// A: 原数组
// n: 原数组大小

Contribute

When building a tree, we use an array to simulate a binary tree to save time and space.
For a tree with rt (rt ≠ 0) rt(rt \neq0)rt(rt=0 ) is the root node of a binary tree, its left and right sons are respectivelyrt ∗ 2 rt * 2in the arrayrt2 andrt ∗ 2 + 1 rt * 2 + 1rt2+1 .
We use bit operations to express (zhuang) to show (bi), that is,rt <<1 rt << 1rt<<1 r t < < 1 ∣ 1 rt << 1 | 1 rt<<1 1 .
In addition, we extract the operation of updating the current node as a functionpushUpto reduce code duplication.

 // 更新当前节点
void pushUp(int rt) {
    Sum[rt] = Sum[rt << 1] + Sum[rt << 1 | 1];
}

// 建树
void Build(int l, int r, int rt) {
    if (l == r) {
        Sum[rt] = A[l];
        return;
    }
    int mid = (l + r) >> 1;
    // 递归建树
    Build(l, mid, rt << 1);
    Build(mid + 1, r, rt << 1 | 1);
    // 建树完成后需要更新当前节点(因为当前节点的子节点的值可能改变,导致当前节点的值需要改变)
    pushUp(rt);
}

Single point of update

Now we need to update the value of a node, what to do after the value of the node is updated? Update the values ​​of the small inter-district, inter-district and large interval where he is located~
Here we update recursively.

/// x is the node needing to be changed.
/// [l, r] is the region now.
void udNode(int x, int val, int l, int r, int rt) {
    if (l == r) {
        Sum[rt] += val;
        return;
    }
    int mid = (l + r) << 1;
    // 确定x节点所在的区间
    if (x <= mid) udNode(x, val, l, mid, rt << 1);
    else udNode(x, val, mid + 1, r, rt << 1 | 1);
    pushUp(rt);
}

Interval update

What if we need to update the value of an interval uniformly? For example, the number of a certain interval increases or decreases a certain number at the same time.
At this time, it is slower to come one by one, and we need to do it as a whole.
Find the intersection interval between the target interval and the large interval, between the cells, and between the small cells, and then update~

/// [L, R] is the wanted region.
/// [l, r] is the region now.
// 这个范围很容易混,建议画图明确一下
void udRegion(int L, int R, int val, int l, int r, int rt) {
	// 在当前区间包含于目标区间时,直接更新就好~
    if (L <= l && r <= R) {
        Sum[rt] += val * (r - l + 1);
        Add[rt] += val;
        return;
    }
    // 否则寻找小区间和小小区间~
    int mid = (l + r) >> 1;
    // 下推lazy标记(下文解释~)
    pushDown(rt, mid - l + 1, r - mid);
    // 这里自己画个图比我啰嗦好懂多了~
    if (L <= mid) udRegion(L, R, val, l, mid, rt << 1);
    if (R > mid) udRegion(L, R, val, mid + 1, r, rt << 1 | 1);
    pushUp(rt);
}

Regarding the above pushDown.

After we update the interval, we will perform a query operation. The range of the query is likely to include the updated range. At this time, when the interval is queried, the updated value will be returned directly. It will not involve inter-cell and small cells. Value between.

So we can be lazy, and temporarily record it here to be updated and not update it temporarily, and update it when it is needed between cells and small cells to save time (it is likely that the next few queries do not need inter-cell It takes time to update between and small communities).

/// ln is the number of the nodes of the Left Subtree
/// rn is the number of the nodes of the Right Subtree
// 下推lazy标记
void pushDown(int rt, int ln, int rn) {
    if (Add[rt]) {
        Add[rt << 1] += Add[rt];
        Add[rt << 1 | 1] += Add[rt];
        Sum[rt << 1] += Add[rt] * ln;
        Sum[rt << 1 | 1] += Add[rt] * rn;
        Add[rt] = 0;
    }
}

Interval query

Query interval [l, r] [l, r][l,r ] interval sum.

If there is a situation where inter-cell/small-cell inter-cell is needed, the lazy mark is pushed down.

// 查询区间和
int Query(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) return Sum[rt];
    int mid = (l + r) >> 1;
    pushDown(rt, mid - l + 1, r - mid);
    int ret = 0;
    if (L <= mid) ret += Query(L, R, l, mid, rt << 1);
    if (R > mid) ret += Query(L, R, mid + 1, r, rt << 1 | 1);
    return ret;
}

Complete code

#include <bits/stdc++.h>

using namespace std;
const int N = 1e5 + 10;
int Sum[N << 2], Add[N << 2];
// Sum: 线段树数组
// Add: lazy标记
int A[N], n;
// A: 原数组
// n: 原数组大小

 // 更新当前节点
void pushUp(int rt) {
    Sum[rt] = Sum[rt << 1] + Sum[rt << 1 | 1];
}

// 建树
void Build(int l, int r, int rt) {
    if (l == r) {
        Sum[rt] = A[l];
        return;
    }
    int mid = (l + r) >> 1;
    // 递归建树
    Build(l, mid, rt << 1);
    Build(mid + 1, r, rt << 1 | 1);
    // 建树完成后需要更新当前节点(因为当前节点的子节点的值可能改变,导致当前节点的值需要改变)
    pushUp(rt);
}

/// x is the node needing to be changed.
/// [l, r] is the region now.
void udNode(int x, int val, int l, int r, int rt) {
    if (l == r) {
        Sum[rt] += val;
        return;
    }
    int mid = (l + r) << 1;
    // 确定x节点所在的区间
    if (x <= mid) udNode(x, val, l, mid, rt << 1);
    else udNode(x, val, mid + 1, r, rt << 1 | 1);
    pushUp(rt);
}

/// ln is the number of the nodes of the Left Subtree
/// rn is the number of the nodes of the Right Subtree
// 下推lazy标记
void pushDown(int rt, int ln, int rn) {
    if (Add[rt]) {
        Add[rt << 1] += Add[rt];
        Add[rt << 1 | 1] += Add[rt];
        Sum[rt << 1] += Add[rt] * ln;
        Sum[rt << 1 | 1] += Add[rt] * rn;
        Add[rt] = 0;
    }
}

/// [L, R] is the wanted region.
/// [l, r] is the region now.
// 这个范围很容易混,建议画图明确一下
void udRegion(int L, int R, int val, int l, int r, int rt) {
    if (L <= l && r <= R) {
        Sum[rt] += val * (r - l + 1);
        Add[rt] += val;
        return;
    }
    int mid = (l + r) >> 1;
    // 下推lazy标记
    pushDown(rt, mid - l + 1, r - mid);
    if (L <= mid) udRegion(L, R, val, l, mid, rt << 1);
    if (R > mid) udRegion(L, R, val, mid + 1, r, rt << 1 | 1);
    pushUp(rt);
}

// 查询区间和
int Query(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) return Sum[rt];
    int mid = (l + r) >> 1;
    pushDown(rt, mid - l + 1, r - mid);
    int ret = 0;
    if (L <= mid) ret += Query(L, R, l, mid, rt << 1);
    if (R > mid) ret += Query(L, R, mid + 1, r, rt << 1 | 1);
    return ret;
}

int main() {

}


Guess you like

Origin blog.csdn.net/qq_45934120/article/details/108029362