Tree line (Segment Tree) Getting Started

  Segment tree is the summation interval, the other problem is very practical maximum or minimum range requirements of an algorithm, which is essentially a binary search tree, interval possible to realize a section partitioned into cells, each cell interval corresponds to a leaf node in the tree line.
  Segment tree can quickly single point, to modify the interval, query, time complexity is O (logN), non-optimized space complexity 2N, 4N in general but also to open an array of practical applications in order to prevent cross-border, it is sometimes required discrete of the compression space.
  Each segment tree node may store a range of about inclusive, certain features may be subject of the request in accordance with the value in the memory section, achievements, modifications, recursive queries are used, the following describes the specific implementation method.
  First, the root node ID is set to 1, the left and right child nodes are numbered 2, 3, and so on, the left and right child node number number of the node i, respectively 2 * i, 2 * i + 1, a node storage section is provided is [L, R], the left and right child nodes are stored in the interval [L, (L + R) >> 1], [(L + R) >> 1 + 1, R] ( ">>" bits the operator right-shift operator, i.e. a movement bit operand specified number of bits to the right, i.e. the right one divided by two, using the appropriate bit in the program operation efficiency can be improved)
To the interval [1, 10] as an example:
 

  Can be obtained from the FIG., The left and right end leaf nodes equal interval stored, the minimum unit of storage;

  Observed number of each node, the node number can be found and not 18-23, as the two children of the same parent node number depends entirely on its parent node ID, rather than sequentially numbered, so not all numbers will be used .

  First need to define a structure, which element comprises a range of about endpoints, the subject and the need to perform required maintenance interval variable, and the interval to the following Example.

   Definition of the structure below

struct segment_tree
{
    int L, R & lt; // left end section 
    int Val, the lazy; // Val interval and is, as the lazy lazy marker, explained later do 
} T [* MAXN . 4 ];

Interval node represents each node stored for all points and, while each parent node is divided into two disjoint intervals, making it easy to come to the interval of the parent node and its two child nodes should and the sum of the interval, the interval and each leaf node is the value of this point, so we can deduce the value of each parent node from bottom to top from the leaf node;

Or in [10], for example, set values ​​a1-a10 are 1 to 10, i.e. leaf nodes value of 1-10, FIG weights can be drawn:

Each node seeking initial period and this step is done while the contribution, contribution implemented using recursion, specific code as follows:

. 1  void Build ( int P, int LL, int RR) // P is the node number, ll, rr interval endpoints were approximately 
2  {
 . 3      T [P] .L = LL;
 . 4      T [P] .r = RR;
 . 5      IF (LL == RR)
 . 6      {
 . 7          T [P] = a .val [LL]; // If a leaf node, and the interval is equal to the point value 
. 8          return ;
 . 9      }
 10      int MID = (LL + RR) >> . 1 ;
 . 11      build (P << . 1 , LL, MID); // build left subtree 
12     Build (P << . 1 | . 1 , MID + . 1 , RR); // build the right subtree 
13 is      T [P] .val T = [P << . 1 ] .val + T [P << . 1 | . 1 ] .val ; // maintenance interval and 
14 }

  The above code "(ll + rr) >> 1" may be written as "(ll + rr) / 2", "p << 1" may be written as "p * 2", "p << 1 | 1" also can be written as "p * 2 + 1"

   Called to build (1,1, n), representing the interval left point of the root node is 1, the right end point is n, then create the left subtree and right subtree followed by recursively, do not forget that after the completion of the establishment and maintenance intervals, that is, approximately equal to the value of the node and child nodes

modify:

  After the completion of our achievements will be carried out to modify the interval, if the traditional methods of violence for each point within the operating range are very easy to modify timeout, and tree line on a good saving time (ps: simple questions such intervals and in seeking the advantages tree line than Fenwick tree is not obvious, Fenwick tree code is more concise, but more versatile tree line, can solve many complex problems with which algorithm can be selected according to the meaning of the questions)

Or in [10], for example, if you want the interval [3,7] are added to each number 3, as follows:

1. From the top down traversal, if the interval represented by this point is contained in [3,7], the interval and modifying it, which do not need to traverse the child node, as shown below, to find [3,3], [4,5], [6,7] contained in [3,7], the [3,3] is a leaf node, just add 3, [4,5], [6,7] contains two element, it is necessary to add 6, the value of the child node is not modified, then if we want to query the value of 4,5,6,7, and how to determine whether it has been modified it? This requires labeled lazy, lazy flag initial value is 0, when we modified [4,5], [6,7] these two sections, to mark both nodes lazy plus 3 are representative of the each point within the section 3 are added, when the query value of its child nodes, will mark decentralized lazy

Wherein the tag is the lazy decentralized process: the value of the flag lazy two child nodes of the node plus the lazy labeled node and two child nodes update interval and, the zero flag lazy node, as follows:

. 1  void pushdown ( int P)
 2  {
 . 3      T [P << . 1 ] .val + = (T [P << . 1 ] .rt [P << . 1 ] .L + . 1 ) * T [P] .lazy;
 . 4      T [P << . 1 | . 1 ] .val + = (T [P << . 1 | . 1 ] .rt [P << . 1 | . 1 ] .L + . 1 ) * T [P] .lazy; // update interval left child node and 
. 5      T [P << . 1 ] + = .lazy T [P] .lazy;
 . 6      T [P << . 1 | . 1 ] .lazy + T = [P] .lazy; //Left and right child nodes labeled lazy updating, note the "+ =" 
. 7      T [P] .lazy = 0 ; // parent node flag is set to zero lazy 
8 }

Note:

  · Lazy mark may overlap, so the update is labeled lazy must remember that Canada, rather than directly equal to, for example, we twice modification operations on an interval, a plus 2, plus 3 once, then it becomes a lazy mark 5 , only 5 lazy decentralized value at query time sub-interval can be labeled;

  · Lazy marker layer by layer is decentralized, that is only one layer of decentralization, rather than directly into the next leaf node

 

2 After finished modifying these intervals and, do not forget to maintain the value of the parent node, as shown below:

End modify the tree were as follows:

The orange is a modified spot can be seen that the complexity of the modified line segment tree is O (logn), much faster than the violence of O (n)

Code is implemented as follows:

. 1  void the Add ( int P, int LL, int RR, int K)
 2  {
 . 3      IF (T [P] .L> = LL && T [P] .r <= RR)
 . 4      {
 . 5          T [P] .val + = ( T [P] .rt [P] + .L . 1 ) * K;
 . 6          T [P] + = .lazy K;
 . 7          return ;
 . 8      } // If this interval is included in the range to be modified, modifying its weight, lazy flag to K 
. 9      IF (T [P] .lazy)
 10          pushdown (P); // decentralized lazy numerals 
. 11      int MID = (T [P] .L + T [P] .r) >> . 1;
 12 is      IF (LL <= MID)
 13 is          the Add (P << . 1 , LL, RR, K); // modified left subtree
 14      IF (RR> MID)
 15          the Add (P << . 1 | . 1 , LL, RR , K); // right subtree modified
 16      T [P] .val T = [P << . 1 ] .val + T [P << . 1 | . 1 ] .val; // maintenance interval and 
17 }

 

Inquire:

For the segment tree for the modified interval after the query to the query interval [5,10], for example, with similar modifications, from there down, if the interval is included in the query section, the interval and the interval plus i.e. may, for this query, we just need to add [5,5] and [6,10] interval and, [6,10] interval and 46, can be directly coupled, and [5,5] values Previous modification has not been modified, it is not directly coupled with 5, the first marker decentralized lazy parent node, the [5,5] updates the value of 8, and then can be added

Queries specific code as follows:

. 1  int Query ( int P, int LL, int RR)
 2  {
 . 3      IF (T [P] .L> T && LL = [P] .r <= RR)
 . 4          return T [P] .val; // if this interval included in the query section, the sections and directly returns 
. 5      int ANS = 0 ;
 . 6      IF (T [P] .lazy)
 . 7          pushdown (P); // decentralized lazy numerals 
. 8      int MID = (T [P] .L T + [P] .r) >> . 1 ;
 . 9      IF (LL <= MID)
 10          ANS + = Query (P << . 1 , LL, RR); //Query left subtree 
. 11      IF (RR> MID)
 12 is          ANS + = Query (P << . 1 | . 1 , LL, RR); // query right subtree 
13 is      return ANS;
 14 }

 

Example: Luo Gu P3372 [template] segment tree 1

AC code is as follows:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define MAXN 100005
 4 struct segment_tree
 5 {
 6     int l,r;
 7     long long val,lazy;
 8 }t[MAXN*4];
 9 int a[MAXN];
10 void build(int p,int ll,int rr)
11 {
12     t[p].l=ll;
13     t[p].r=rr;
14     if(ll==rr)
15     {
16         t[p].val=a[ll];
17         return;
18     }
19     int mid=(ll+rr)>>1;
20     build(p<<1,ll,mid);
21     build(p<<1|1,mid+1,rr);
22     t[p].val=t[p<<1].val+t[p<<1|1].val;
23 }
24 void pushdown(int p)
25 {
26     t[p<<1].val+=(t[p<<1].r-t[p<<1].l+1)*t[p].lazy;
27     t[p<<1|1].val+=(t[p<<1|1].r-t[p<<1|1].l+1)*t[p].lazy;
28     t[p<<1].lazy+=t[p].lazy;
29     t[p<<1|1].lazy+=t[p].lazy;
30     t[p].lazy=0;
31 }
32 void add(int p,int ll,int rr,int k)
33 {
34     if(t[p].l>=ll&&t[p].r<=rr)
35     {
36         t[p].val+=(t[p].r-t[p].l+1)*(long long)k;
37         t[p].lazy+=k;
38         return;
39     }
40     if(t[p].lazy)
41         pushdown(p);
42     int mid=(t[p].l+t[p].r)>>1;
43     if(ll<=mid)
44         add(p<<1,ll,rr,k);
45     if(rr>mid)
46         add(p<<1|1,ll,rr,k);
47     t[p].val=t[p<<1].val+t[p<<1|1].val;
48 }
49 long long query(int p,int ll,int rr)
50 {
51     if(t[p].l>=ll&&t[p].r<=rr)
52         return t[p].val;
53     long long ans=0;
54     if(t[p].lazy)
55         pushdown(p);//下放懒标记
56     int mid=(t[p].l+t[p].r)>>1;
57     if(ll<=mid)
58         ans+=query(p<<1,ll,rr);
59     if(rr>mid)
60         ans+=query(p<<1|1,ll,rr);
61     return ans;
62 }
63 int main()
64 {
65     int n,m,x,y,k,i,flag;
66     long long sum;
67     cin>>n>>m;
68     for(i=1;i<=n;i++)
69         scanf("%d",&a[i]);
70     build(1,1,n);
71     while(m--)
72     {
73         scanf("%d",&flag);
74         if(flag==1)
75         {
76             scanf("%d%d%d",&x,&y,&k);
77             add(1,x,y,k);
78         }
79         else
80         {
81             scanf("%d%d",&x,&y);
82             sum=query(1,x,y);
83             cout<<sum<<endl;
84         }
85     }
86     return 0;
87 }
View Code

 

Author:hiang  Date:2019.5.26

Guess you like

Origin www.cnblogs.com/CSGOBESTGAMEEVER/p/10924086.html