Segment Tree + Lazy Tag Summary

The line segment tree is a very basic algorithm...

Line segment tree is a kind of binary tree, which can be regarded as a variant of tree-like array. It first appeared in 2001 and was invented by program contestants. Our teacher ZS said: "All problems that can be solved by tree arrays can be solved by line segment trees, but some problems that can be solved by line segments cannot be solved by tree arrays." It can be seen that line segment trees are very powerful...

From: https://skywt.cn/posts/segment-tree/

This data structure has little practical use, but is widely used in programming competitions due to its ease of programming. Its use is in O ( log N ) Query the information in a specified interval, and support operations such as updating at the same time complexity.
--Wikipedia

example

Let's take a look at the line segment tree template title on Luogu: [Template] Line segment tree 1

Topic description

For example, given a sequence of numbers, you need to perform the following two operations:
1. Add x
to each number in an interval 2. Find the sum of each number in an interval

Input and output format

Input format:
The first line contains two integers N and M, which respectively represent the number of numbers in the sequence and the total number of operations.
The second line contains N space-separated integers, where the ith number represents the initial value of the ith item of the sequence.
The next M lines each contain 3 or 4 integers, representing an operation, as follows:
Operation 1: Format: 1 xyk Meaning: Add k to each number in the interval [x,y]
Operation 2: Format: 2 xy Meaning: output the sum of each number in the interval [x,y]

Output format:
The output contains several lines of integers, which are the results of all operations 2.

Input and output example

input sample

5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4

Sample output

11
8
20

illustrate

Space-time limit: 1000ms, 128M
Data size:
For 30% of the data: N<=8, M<=10
For 70% of the data: N<=1000, M<=10000
For 100% of the data: N<=100000, M<=100000
(The data has been enhanced ^_^, guaranteed to be within the int64/long long data range)
Example description:
Sample description

Definition and construction of segment tree

A segment tree is a balanced binary tree, and the distance between all leaves and the root is at most 1. Let the length of the entire interval be N, then it has N leaf nodes, each leaf node represents a unit interval, and the interval represented by each internal node is the union of its two sons representing the interval.

Translated into human words: the line segment tree is a binary tree, each node represents an interval (perhaps it should be called an interval tree...), and the size of the interval represented by the leaf node is 1 (that is, the unit interval). (Take the example above as an example) It is easy to understand that according to the leaf nodes, we can accumulate upwards. For each non-leaf node, it is easy to construct the sum of all numbers in the interval represented by it (hereinafter referred to as the "summation of jurisdictional intervals" ( My own name...because I think the word "jurisdiction" is very high-end...), and use tree[x] to represent the sum of the jurisdictional interval of the x node) (that is, the left and right sons of each point represent the sum of the interval sum). In this way, we can construct a line segment tree . This is how the build function constructs the segment tree:

inline void build(int p,int tl,int tr){
    tag[p]=0;
    if (tl==tr) {tree[p]=a[tl];return;}
    int mid=((tr-tl)>>1)+tl;
    build((p<<1)  ,tl,mid  );
    build((p<<1)+1,mid+1,tr);
    tree[p]=tree[p<<1]+tree[(p<<1)+1];
}

Interval Updates and Lazy Markers

If there is no interval update, we don't need lazy markup, because for single-point update, push up directly, O ( log 2 N ) results in time. However, interval updating is very troublesome: if a single point is updated one by one, the maximum required O ( N log 2 N ) ! Apparently it's easy to time out. Lazy Tag (Lazy Tag) is actually an optimization of the simple line segment tree, which can make the interval update also reach O ( log 2 N ) level. So first let's get to know the lazy markup:

lazy tag

Lazy Tag (Lazy Tag), also translated as "delayed tag" in some places (sounds high-end...), means that when updating a part of the interval, each point in the interval is not updated, but (lazy) This interval is divided into many intervals that can be represented by points on the line segment tree (of course, the fewer intervals the better, that is to say, the larger each interval is, the better), only the points representing this interval are corrected, and then these intervals are divided Under the mark, next time if the interval to be queried intersects with this interval and does not contain this interval, it means that this interval needs to be "divided", and then the interval is revised downward. In this way, the time complexity can be optimized a lot.

How to perform interval update

As mentioned just now, if you want to add delta to all the elements in the interval [L, R], you only need to start from the root node of the line segment tree and search down to find the first interval contained by [L, R], then Correct this interval (note that "correcting this interval" only increases the mark and lazy mark of the interval sum, not correcting the interval downward), and then continue to search down until the found section of interval has [L, R] covered.

Interval query

The interval query is somewhat similar to the interval update. Suppose you want to query [L, R], and also start from the root node and "split" down, the difference is that the lazy mark just now may need to be used for downward correction. (see code for details)

Full code (annotated version)

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=2000005;
int n,m;
long long a[maxn],tree[maxn],tag[maxn];
inline int read(){
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
inline long long llread(){
    long long ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=(long long)-1;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*(long long)10+(long long)(ch-'0'),ch=getchar();
    return (long long)ret*f;
}
inline void build(int p,int tl,int tr){  //构造Segment Tree 
    tag[p]=0;
    if (tl==tr) {tree[p]=a[tl];return;}
    int mid=((tr-tl)>>1)+tl;
    build((p<<1)  ,tl,mid  );
    build((p<<1)+1,mid+1,tr);
    tree[p]=tree[p<<1]+tree[(p<<1)+1];
}
inline void push_down(int tl,int tr,int p){  //push_down函数负责向下传递,将存储的tag数组用于修正 
    int mid=((tr-tl)>>1)+tl;
    tree[p<<1]+=(long long)tag[p]*(mid-tl+1);  //只需要修正一对左右儿子,不需要一直传递到叶节点 
    tag[p<<1]+=tag[p];  //因为没有往下修正,所以要把儿子的tag累计上 
    tree[(p<<1)+1]+=(long long)tag[p]*(tr-mid);
    tag[(p<<1)+1]+=tag[p];
    tag[p]=0;
}
inline void update(int sl,int sr,int tl,int tr,long long delta,int p){
    if (sl<=tl&&sr>=tr){  //p管辖的区间在修改范围内 
        tree[p]+=delta*(long long)(tr-tl+1);
        tag[p]+=delta;  //tag数组记录剩下的未向下传递的,实现“lazy tag”
        return;
    }
    push_down(tl,tr,p);  //如果没有完全包含p管辖的区间,则说明应该向下“分割”,所以应该用tag数组先修正下面的tree[] 
    int mid=((tr-tl)>>1)+tl;  //分割[tl,tr]区间 
    if (sl<=mid) update(sl,sr,tl,mid,delta,p<<1);
    if (mid<sr)  update(sl,sr,mid+1,tr,delta,(p<<1)+1);
    tree[p]=tree[p<<1]+tree[(p<<1)+1];  //向上累计 
}
inline long long query(int sl,int sr,int tl,int tr,int p){  //查询操作 
    if (sl<=tl&&sr>=tr) return tree[p];  //如果完全包含p所管辖的区间则直接返回tree[p] 
    long long ret=0;  //否则就“分割” 
    push_down(tl,tr,p);  //先要把当前的tag用于修正下面两个儿子,这样才能保证下面query用到的tree[]是正确的 
    int mid=((tr-tl)>>1)+tl;
    if (sl<=mid) ret+=(long long)query(sl,sr,tl,mid,p<<1);
    if (mid<sr)  ret+=(long long)query(sl,sr,mid+1,tr,(p<<1)+1);
    return ret;
}
int main(){
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    n=read();m=read();
    for (int i=1;i<=n;i++) a[i]=llread();
    build(1,1,n);
    for (int i=1;i<=m;i++){
        int c=read();
        if (c==1){
            int x=read(),y=read();
            long long z=llread();
            update(x,y,1,n,z,1);
        } else
        if (c==2){
            int x=read(),y=read();
            printf("%lld\n",query(x,y,1,n,1));
        }
    }
    return 0;
}

refer to

https://www.luogu.org/blog/pks-LOVING/senior-data-structure-qian-tan-xian-duan-shu-segment-tree
https://baike.baidu.com/item/%E7%BA%BF%E6%AE%B5%E6%A0%91/10983506
https://zh.wikipedia.org/zh/%E7%B7%9A%E6%AE%B5%E6%A8%B9_(%E5%84%B2%E5%AD%98%E5%8D%80%E9%96%93)

From: https://skywt.cn/posts/segment-tree/

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324664963&siteId=291194637