Algorithms - segment tree

First, the introduction of

  We need some maintenance often encounter a series of problems, for example, given a sequence of integers, each operation will modify the sequence number on a location, or ask you all numbers within a certain range and sequence. With "violence" algorithm, the complexity of the modified single-point is O (. 1), a single interrogation interval and the complexity is O (interval length). Prefix and algorithms, and interrogation interval is O (. 1), but modified single-point complexity is O (interval length). m such problems (query number) and n (interval length) is often of the order of 10 ^ 5, but two algorithms will fail. Segment tree is used to process like this, a single point on a modified sequence, data structures, etc. interrogation zone, corresponding to the simple algorithm complexity O (n ^ 2) time, the segment tree can complexity O (nloogn) of under solve the problem.

Second, the concept

  Segment tree is a binary tree, the tree segments corresponding to each node is defined by a sequence interval. as the picture shows:

  Easily found, the root node corresponds to the whole interval [0, n-1]. If a node corresponding to the interval [l, r], when l = r, which is a leaf node, there is no left son, or there must be left son, mid = (l + r) / 2, the left son of the corresponding the interval [1, mid], the right son of the corresponding section is a [mid + 1, r].

  As can be seen from this, the last layer of n-node and right, the penultimate layer n / 2 nodes, and so on, the number of nodes in the segment tree is n + n / 2 + n / 4 + .. . + 1 = 2n-1.

So that segment tree height h, h segment tree is easy to see only logn level. When we need to maintain the sequence length is an integer power of 2, segment tree is a full binary tree. In other cases, segment tree h-1 is a full binary tree layer, the last layer may be dissatisfied.

Third, application examples

  Problem: given sequence a [0], a [1], a [2] ... a [n-1] has the following operation m times, a single point can be modified, and demand interval.

1, the definition of the tree

  Definition of a node comprises l, r, w, f.

struct node
{
    int L, R & lt, W, F; // F flag is lazy, can not see. 
Tree} [N * . 4 + . 5 ];

 

2, achievements

void Build ( int K, int L, int R & lt) { // K is the current node, l is the current left boundary, r is the current right boundary 
    Tree [K] .L = L; Tree [K] .r = R & lt;
     IF (L == R & lt) {Scanf ( " % D " , & Tree [k] .W); return ;} // L == R & lt k is a leaf node. 
    int MID = L + R & lt >> . 1 ; // L + R & lt >>. 1 = (L + R & lt) / 2 
    Build (K << . 1 , L, MID); // left son k << 1 = k * 2 
    Build (K << . 1 | . 1 , MID + . 1 , R & lt); // the right son. 1 << K | = K. 1. 1 + 2 * 
    Tree [K] .W = Tree [K << . 1] .W + Tree [K << . 1 | . 1 ] .W; // statistics node value 
}

 

Mark next pass (you can first do not understand)

void pushdown(int k){
    tree[k<<1].f+=tree[k].f;
    tree[k<<1|1].f+=tree[k].f;
    tree[k<<1].w+=tree[k].f*(tree[k<<1].r-tree[k<<1].l+1);
    tree[k<<1|1].w+=tree[k].f*(tree[k<<1|1].r-tree[k<<1|1].l+1);
    tree[k].f=0;    
}

 

4, and ask the interval

int SUM ( int K, int X, int Y) { // X, Y for the interrogation interval 
    int ANS = 0 ;
     IF (Tree [K] .L> Tree && X = [K] .r <= Y) if the current node { point x, y in. Returns the current value.
        return Tree [K] .W;
    }
    IF (Tree [K] .F) pushdown (K); // each pass to be labeled downstream 
    int MID = (Tree [K] .L + Tree [K] .r) >> . 1 ;
     IF (X <= MID) + ANS = SUM (K << . 1 , X, Y); // if x <= mid DESCRIPTION left son of the current interval 
    IF (Y> MID) + ANS = SUM (K << . 1 | . 1 , X, Y); // if y> mid illustrate the current interval son 
    return ANS;
}

 

5, a single point of modification

void the Add ( int K, int x, int V) { // node x plus 
    IF (tree [k] .L == tree [k] .r) { // if the currently traversed node 
        tree [k] + = V .W; // current node plus V 
        return ;
    }
    IF (Tree [K] .F) pushdown (K); // tag downstream 
    int MID = Tree [K] .L + Tree [K] .r >> . 1 ;
     IF (X <MID) the Add (<< K 1 , the X-, v); // if x <mid son then left 
    the else the Add (k << 1 | 1 , the X-, v); // otherwise in the right son 
    tree [k] .w = tree [ k << . 1 ] .W + Tree [K << . 1 | . 1 ] .W; // update interval value 
    return ;
}

 

6, modify the interval

Sometimes we need to modify the interval interval asked to use the above operation has been unable to complete our needs. We can not modify the efficient completion interval, then we have to introduce a thing called lazy marker, is to maintain a value of f at each node, each node represents an interval corresponding to all the trees together with f, when traversing the when the flag then pass to the next node.

Noting the root to a leaf node [i, i] will go through the path section comprises all the points i corresponding to the nodes, and all points on the path comprising i will in this position, so we will add the path passed f it is the location of the current value of i. And only on the path O (logn) point, so the complexity can withstand.

  Values ​​not selected until positions immediately update all modifications, but the modifications affect the recording worth at a node on the path to the root, the nodes wait until a query is generated to all this position a full path Effect of combined values, the required results are obtained, optimizing the clever time complexity. Modify the code interval is given below.

void add_p ( int K, int X, int Y, int V) { // interval x, y plus V 
    IF (Tree [K] .L> Tree && X = [K] .r <= Y) { // If included interval plus a current lr v, and mark superscript 
        Tree [K] + = .W (Tree [K] .r-Tree [K] + .L . 1 ) * V;
        tree[k].f+=v;
        return ;
    }
    IF (Tree [K] .F) pushdown (K); // tag downstream 
    int MID = Tree [K] .L + Tree [K] .r >> . 1 ;
     IF (X <= MID) add_p (K < < . 1 , X, Y, V); // left son 
    IF (Y> MID) add_p (K << . 1 | . 1 , X, Y, V); // the right son 
    tree [k] .w = tree [ k << 1 ] .w + Tree [k << 1 | 1 ] .w; // recount w 
    return ;
}

 

7, example

Luo Gu link

#include<bits/stdc++.h>
using namespace std;
const int N=1000005,logN=20;
int x,y,n,m,Log[N],f[N][logN],a[N];
inline int read(){
    int ret=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
    while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
int main(){
//     freopen("p.in","r",stdin);
//     freopen("p.out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=n;i++)a[i]=read();
    Log[0]=-1;
    for(int i=1;i<=n;i++)f[i][0]=a[i],Log[i]=Log[i>>1]+1;
    for(int j=1;j<=logN;j++)
    for(int i=1;i+(1<<j)-1<=n;i++){
        f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
    }
    for(int i=1;i<=m;i++){
        x=read(),y=read();
        int s=Log[y-x+1];
        printf("%d ",min(f[x][s],f[y-(1<<s)+1][s]));
    }
    return 0;
}

 

void add(int k,int x,int v){if(tree[k].l==tree[k].r){tree[k].w+=v;return ;}if(tree[k].f)pushdown(k);int mid=tree[k].l+tree[k].r>>1;if(x<mid)add(k<<1,x,v);else add(k<<1|1,x,v);tree[k].w=tree[k<<1].w+tree[k<<1|1].w;return ;}

Guess you like

Origin www.cnblogs.com/booksBlog/p/11004989.html