Data structures: segment tree

 From "algorithm contest Advanced Guide."

Segment tree is based on the idea of ​​divide and conquer binary tree for statistical information on the range.

The basic features of the segment tree:
each node of the tree represents 1. a segment interval.
2. interval segment tree with a unique root node represents the entire range of statistics, such as [1, N].
3. Each leaf node of the tree represents a segment length of meta interval [x, x] 1 is.
4. For each internal node [l, r], its left child node [l, mid], the right child node [mid + 1, r], where mid = (l + r) / 2 ( down Rounding).

Method segment tree node number: "father twice" node numbering
1. root numbered 1.
2. The number of the left child node number of node x x * 2, the right child node number x * 2 + 1.

Note: Save the tree line array length is less than either 4N to ensure not out of range. (N leaf nodes)

The basic operation of the segment tree (interval maximum value to maintain Example)

 Segment tree of achievements

  • It creates a segment tree and the maximum value stored in the corresponding section on each node.
struct SegmenTree {
     you to, to;
    you dat;
T} [N * . 4 ]; // struct array storage segment tree

void build(int p,int l,int r){
    T [p] .L = L, T [p] .r = R & lt; // node p represents an interval [L, R & lt] 
    IF (L == R & lt) {T [p] .dat A = [L]; return ;} // leaf node 
    int MID = (L + R & lt) / 2 ; // binary 
    Build (P * 2 , L, MID); // left child node [l, mid], No. 2 * P 
    Build (P * 2 + . 1 , MID + . 1 , R & lt); // right child node [mid + 1, r], No. 1 + 2 * P 
    T [P] .dat = max (T [P * 2 ] .dat, T [P * 2 + . 1 ] .dat); // up information transmitted from the 
}

Build ( . 1 , . 1 , n-); // call inlet

 

 

Single-point segment tree changes

  • The a [x] modify the value v, the time complexity: O (log N).

Line segment tree, the root node (Node No. 1) is to perform various instructions inlet. We need to start from the root, recursively find the leaf nodes represent the interval [x, x], and then up the updating information stored on [x, x] and all its ancestor nodes from under.

void Change ( int P, int X, int V) {
     IF .dat = V (T [P] == T .L [P] .r) {T [P]; return ;} // find the leaf node 
    int = MID (T [P] .L + T [P] .r) / 2 ;
     IF (X <= MID) Change (* P 2 , X, V); // X belonging to a left half section 
    the else Change (* P 2 + . 1 , X, V); // X belongs right interval 
    T [P] .dat = max (T [P * 2 ] .dat, T [P * 2 + . 1 ] .dat); // from up update information 
}

Change ( . 1 , X, V); // call inlet 

 

 Range segment tree of query

  •  A maximum value of the query sequence in the interval [l, r] of.

Starting from the root, the following recursive procedure:
1. If [l, r] completely covers the interval represented by the current node, backtracking immediately, and the node value dat candidate answer.
2. If the left child node and [l, r] has an overlapping portion, the left child node recursively visit.
3. If the right child node, and [l, r] overlap section, then descend the right child node.

int ASK ( int P, int L, int R & lt) {
     IF (<T = [P] && R & lt .L> T L = [P] .r) return T [P] .dat; // completely contained 
    int MID = (T [P] .L + T [P] .r) / 2 ;
     int Val = - ( . 1 << 30 ); // negative infinity 
    IF (L <= MID) Val = max (Val, ASK (P * 2 , L, R & lt)); // the left child node overlap 
    IF (R & lt> MID) Val = max (Val, ASK (P * 2 + . 1 , L, R & lt)); // right child nodes overlap 
    return Val;
} 

COUT << ASK ( . 1 , L, R & lt) << endl; // call inlet 

 

The query procedure the interrogation interval [l, r] into the segment tree green O (log N) nodes , takes a maximum value thereof as the answer.
Reason: At each node [P L , P_R], the set = MID (P L + P R & lt ) / 2 (rounded down), the following situations may occur:
1.l≤p L ≦ P R & lt ≤r, that is completely covered by the current node, direct return.
2.P l ≤l≤p R & lt ≤R, i.e. in the node is only l.
(1) l> mid, only recursively right subtree.
(2) l≤mid, although the two sub-tree recursively, but returns the right child node directly after the recursion.
3.l≤p L ≤r≤p r , i.e., r is only in the node, similarly to the case 2.
4.p l ≤l≤r≤ r , i.e., r and l are in among nodes.
(1) l, r are located on one side of mid, only recursive subtree.
(2) l, r are located on both sides of the mid recursively about two subtrees.

  • Only 4 cases (2) will actually generate a recursive for about two sub-tree. This occurs at most once, then it will become in the case of 2 or 3 child nodes. Thus, the query time complexity of the above-described process is O (2logN) = O (log N).

[1] example: you can answer these questions it
[example 2]: Interval greatest common divisor

Delayed mark (the face of the interval modification)

When we modify instruction, can l≤p L ≤ P L returned the case ≤r immediately, but before backtracking to increase p tag node, identifies "the node had been modified, but its children has not been updated." .
In a subsequent instruction, recursively downward from the node p, we check whether the flag p. If labeled, according to two child nodes labeled p updated information, while increasing the two child nodes labeled p, and p clear indicia.
Thus, the time complexity of each instruction is modified from O (N) down to O (log N).

[Example]: a simple integer problems
main code is as follows:

#define 100000+10

struct N SegmentTree{
    int l,r;
    long long sum,add;
    #define l(x) tree[x].l
    #define r(x) tree[x].r
    #define add(x) tree[x].add
    #define sum(x) tree[x].sum
}tree[N*4];

int a[N],n,m;

void build(int p,int l,int r){
    l(p)=l,r(p)=r;
    if(l==r){sum(p)=a[l];return;}
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    sum(p)=sum(p*2)+sum(p*2+1);
} 

void Spread ( int p) {
     IF (the Add (p)) { // node p marked 
        SUM (p * 2 ) + = the Add (p) * (R & lt (p * 2 ) -l (p * 2 ) + . 1 ); // update information left child node 
        SUM (P * 2 + . 1 ) + = the Add (P) * (R & lt (P * 2 + . 1 ) -l (P * 2 + . 1 ) + . 1 ); // update the right child node 
        the Add (P * 2 ) + = the Add (P); // play delay marker to the left child node of 
        the Add (P * 2 + . 1 ) + = the Add (P); //To the right child playing flag delay 
        the Add (P) = 0 ; // clear the flag b 
    }
}

void Change ( int P, int L, int R & lt, int D) {
     IF (L <= L (P) && R & lt> = R & lt (P)) { // completely covered 
        SUM (P) + = ( Long  Long ) D * (R & lt (P) -l (P) + . 1 ); // update the node information 
        the Add (P) = D +; // to delay playing the node labeled 
        return ;
    }
    Spread (P); // downstream delay flag 
    int MID = (L (P) R & lt + (P)) / 2 ;
     IF (L <= MID) Change (* P 2 , L, R & lt, D);
     IF ( R & lt> MID) Change (* P 2 + . 1 , L, R & lt, D);
    sum(p)=sum(p*2)+sum(p*2+1);
}

long long ask(int p,int l,int r){
    if(l<=l(p)&&r>=r(p))return sum(p);
    Spread (P); // downstream delay flag 
    int MID = (L (P) + R & lt (P)) / 2 ;
     Long  Long Val = 0 ;
     IF (L <= MID) Val + = ASK (P * 2 , L , R & lt);
     IF (R & lt> MID) ASK = Val + (P * 2 + . 1 , L, R & lt);
     return Val;
}

int main () {
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    build(1,1,n);
    while(m--){
        char op[2];int l,r,d;
        cin >> >> l >> r;
        if (in [ 0 ] == ' C ' ) {
            cin>>d;
            change(1,l,r,d);
        }
        else cout<<ask(1,l,r);
    }
}

 

Scan line

[1] example: Atlantis
[2] example: the stars in the window

Guess you like

Origin www.cnblogs.com/zhengchang/p/xianduanshu1.html