On the tree line and LuoguP3372 template [1] tree line solution to a problem

Original title Portal

This question is regarded as the most classic of the segment tree exercises.

Although the method may further Fenwick tree, block, etc. easier to complete,

But the tree line on the efficiency and ease of comprehension has certain advantages.

Segment tree conceptual

Segment tree is a binary search tree, interval tree, and similar, it is divided into a number of unit intervals interval, a leaf node corresponding to each unit section of the segment tree.

For each non-leaf node segment tree  [A, B] [ A , B ], the interval its left son represented by  [A, (A + B) / 2] , the right son represented interval [ ( A + B ) / 2 + . 1 , B ].

Therefore segment tree is a balanced binary tree, the last number of child nodes is  N  , i.e., the entire length of the line section.

Using the segment tree can quickly find a particular node number appearing in a number of line segments, the time complexity of  O (logN)  .

Without the optimization of space complexity  2N , so sometimes you need to make discrete space compression.

It is seen from the perspective of line interval  [1,8] of the segment tree structure:

Yellow is the root node, the green is a leaf node.

Observation segment tree leaf node, we can find the left point and the right point equal intervals leaf node, which is why it has not left child and right child of reason.

The basic operation of the segment tree

1. achievements

Q  : node of a tree line and what elements of it?

A  : Basic should have left point, right point, the value of the node.

And the value of the segment tree node with one block each are the same, need to be changed according to the needs of the subject.

Q  : That's a whole segment tree constructed by what form it?

A  : Structure of the segment tree is a binary tree, it is possible to start building from the root node, its left child node recursively,

(Ie equal to the left point right point) until the current node is a leaf node will no longer continue to build down.

When a node with a return value, the value is returned to the parent node, the value obtained from the parent node to its left end point (i.e., the current node), and the right end point.

And the value of the right and grandparent nodes obtained by its left node (i.e. the parent node) ......

While establishing segment tree back to the value determined at each node and node structure constructed around the tree line.

Next, look at the code:

. 1  void bulid ( int T, int L, int R & lt)
 2  {
 . 3      Tree [T] .L = L; Tree [T] .r = R & lt;     // to has a left node and the node assignments 
. 4      IF (R & lt == L )     // if the leaf node is no longer recursive 
. 5      {
 . 6          Tree [T] .val = A [L];
 . 7          return ;
 . 8      }
 . 9      int MID = L + R & lt >> . 1 ;
 10      bulid (T * 2 , L , MID); // recursive left node 
. 11      bulid (T * 2 + . 1 , MID +. 1 , R & lt); // recursive right node 
12 is      Tree [T] .val Tree = [T * 2 ] + Tree .val [T * 2 + . 1 ;] .val // evaluated after back 
13 }

 

2. Modify the value of the interval

Let's look at an example:

Modified value of [2,6] is:

analysis:

We want to change the value of [2,6] of

{

  You have to first find the interval [2,6];

   {

    因为[2,6]在线段树中不一定是一个整区间,即可能涵盖几个区  间,又可能涵盖不是整个区间的子区间,

    所以我们要想找到区间[2,6]就要找到哪些区间包括了[2,6]的子区间;
    {
        我们要想找到哪些区间涵盖了[2,6]的子区间就要遍历整个线段树;
        {

            我们要遍历整个线段树就得从根节点开始往左右递归;
            {
            我们要递归就要找到终止的条件——当当前区间和要查找的区间[2,6]完全没有交集的时候就不必再往下递归。
            }

        }

    }
}
再修改区间[2,6]的值
{
    [2,6]所覆盖的部分由两种情况组成:
    {
        1.不完整的区间
        {
            暴力修改区间里面每一个数的值;
        }
        2.完整的区间
        {
            这里就是线段树效率快的精髓:

            我们不一定要继续往下递归修改子区间的值,因为即使修改了子区间的值,也不一定会用到;

            同分块的整体标记一样,那么我们就只修改区间的值,我们添加一个元素add(延迟标记)初始值为0.

            add是延迟标记,表示当前节点的子节点还没有更新,

      add在要把整个区间修改的时候用到。使用时我们不修改子区间的值,只在当前区间的add加上应该加的值。

      也就是说,add的作用是以修改它的值来简化修改每一个子区间的值,先把这个事情延迟不做,因为并不是必要的。 自己的子节点还要加上add(注意,add是给子节点用的,不是给自己用的。) 那如果需要查找子节点的时候呢? 那么我们就把子节点应该家的值加上去,并取消当前节点的值。 注意,子节点也要被打上延迟标记,因为我们是修改了子节点的值, 而子节点的子节点的值和子节点的子节点的子节点的值和子节点的子节点的子节点的子节点的值等等仍然没有修改。 } } }

}

After such an analysis, summed up the code:

 1 void spread(int t){ //取消延迟标记(标记下传)
 2     if(tree[t].mark)    //如果当前节点有标记
 3     {
 4         tree[t*2].val+=tree[t].mark*(tree[t*2].r-tree[t*2].l+1);    //更新左节点的值
 5         tree[t*2+1].val+=tree[t].mark*(tree[t*2+1].r-tree[t*2+1].l+1);  //更新右节点的值
 6         tree[t*2].mark+=tree[t].mark;   //左节点被打上标记
 7         tree[t*2+1].mark+=tree[t].mark; //右节点被打上标记
 8         tree[t].mark=0; //取消标记
 9     }
10 }
11 
12 void change(int t,int x,int y,int k)
13 {
14     if(x<=tree[t].l && y>=tree[t].r)    //如果这个区间包含了待修改区间的一部分
15     {
16         tree[t].val+=(long long)k*(tree[t].r-tree[t].l+1);  //修改它的值
17         tree[t].mark+=k;    //打上延迟标记
18         return;
19     }
20     spread(t);  //延迟标记下传
21     int mid=tree[t].l+tree[t].r>>1;
22     if(x<=mid) change(t*2,x,y,k);   //修改左节点的值
23     if(y>mid) change(t*2+1,x,y,k);  //修改右节点的值
24     tree[t].val=tree[t*2].val+tree[t*2+1].val;      //由左节点和右节点推出当前节点的值。
25 }

 

询问区间的值

再来看一个例子:

查询[2,6]的值(区间和):

查询本质上还是需要查找的。

而查找只是修改的一部分,所以查询操作比修改操作会简单很多的。

还是来看一看分析:

{

同修改一样,我们要找到涵盖了[2,6]的一部分的区间;
{
    怎么找呢?我们还是递归查找左右节点,如果当前区间与要抄找的区间[2,6]完全没有交集,就停止递归。

    如果我们找到了一个区间被[2,6]所覆盖,我们就返回它的交集的每个元素的和,表示这是答案的一部分。

}

}

思路还算是自然的。

然后贴上代码:

 1 long long ask(int t,int x,int y)    //注意到数据大小
 2 {
 3     if(x<=tree[t].l && y>=tree[t].r) return tree[t].val;    //被要查找的区间所覆盖
 4     spread(t);  //为了递归子节点,就下传延迟标记
 5     int mid=tree[t].l+tree[t].r>>1;
 6     long long ans=0;
 7     if(x<=mid) ans+=ask(t*2,x,y);   //如果答案的一部分在左节点那边,就递归左节点
 8     if(y>mid) ans+=ask(t*2+1,x,y);  //如果答案的一本分在右节点那边,就递归右节点
 9 
10      //注意:因为答案可能就是左边一部分,右边一部分,所以这两种情况是不矛盾的的,于是不用加上else;
11     return ans;
12 }

 

main函数

main函数完全是由着题意来得到的,应该没有思维难度。

int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    bulid(1,1,n);
    while(m--)
    {
        int op,x,y,k;
        cin>>op;
        if(op==1)
        {
            cin>>x>>y>>k;
            change(1,x,y,k);
        }
        else
        {
            cin>>x>>y;
            cout<<ask(1,x,y)<<endl;
        }
    }
    return 0;
}

 

最后贴上高清的Code:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int a[100005];
 4 struct T
 5 {
 6     int l,r;
 7     long long val,mark;
 8 }tree[400005];
 9 void bulid(int t,int l,int r)
10 {
11     tree[t].l=l;tree[t].r=r;
12     if(l==r)
13     {
14         tree[t].val=a[l];
15         return;
16     }
17     int mid=l+r>>1;
18     bulid(t*2,l,mid);
19     bulid(t*2+1,mid+1,r);
20     tree[t].val=tree[t*2].val+tree[t*2+1].val;
21 } 
22 void spread(int t){
23     if(tree[t].mark)
24     {
25         tree[t*2].val+=tree[t].mark*(tree[t*2].r-tree[t*2].l+1);
26         tree[t*2+1].val+=tree[t].mark*(tree[t*2+1].r-tree[t*2+1].l+1);
27         tree[t*2].mark+=tree[t].mark;
28         tree[t*2+1].mark+=tree[t].mark;
29         tree[t].mark=0;
30     }
31 }
32 
33 void change(int t,int x,int y,int k)
34 {
35     if(x<=tree[t].l && y>=tree[t].r)
36     {
37         tree[t].val+=(long long)k*(tree[t].r-tree[t].l+1);
38         tree[t].mark+=k;
39         return;
40     }
41     spread(t);
42     int mid=tree[t].l+tree[t].r>>1;
43     if(x<=mid) change(t*2,x,y,k);
44     if(y>mid) change(t*2+1,x,y,k);
45     tree[t].val=tree[t*2].val+tree[t*2+1].val;   
46 }
47 long long ask(int t,int x,int y)
48 {
49     if(x<=tree[t].l && y>=tree[t].r) return tree[t].val;
50     spread(t);
51     int mid=tree[t].l+tree[t].r>>1;
52     long long ans=0;
53     if(x<=mid) ans+=ask(t*2,x,y);
54     if(y>mid) ans+=ask(t*2+1,x,y);
55     return ans;
56 }
57 
58 int main(){
59     int n,m;
60     cin>>n>>m;
61     for(int i=1;i<=n;i++)
62         cin>>a[i];
63     bulid(1,1,n);
64     while(m--)
65     {
66         int op,x,y,k;
67         cin>>op;
68         if(op==1)
69         {
70             cin>>x>>y>>k;
71             change(1,x,y,k);
72         }
73         else
74         {
75             cin>>x>>y;
76             cout<<ask(1,x,y)<<endl;
77         }
78     }
79     return 0;
80 }

 

 

Guess you like

Origin www.cnblogs.com/chengyurui/p/11297916.html