Segment tree, powerful data structure, the use is also relatively broad.
First of all, we need to understand what a line tree is.
Line tree, line, there is a left and right end point, then it can certainly represent an interval, then a lot of things on the interval can be used to engage in it, such as: Interval plus, interval multiplication, interval summation.
First let's look at a model of the line segment tree.
, this is the model of a tree of line segments.
The dots in the circle indicate that this is the first point, and red indicates the range of intervals represented by this point.
The number of each point and its left and right two sons is a definite relationship:
Point N, its left son is numbered n$\times$2, and the right son is numbered n$\times$2+1.
Segment Tree supports single-point modification, interval modification, single-point query, interval query.
The explanation is easy to be difficult.
Let's put a picture behind the example (the number in each circle represents the and of the interval).
Building a segment tree frame
Assuming a sequence of length N, we need to maintain a line segment that is 1--n long.
For each point, we need to determine the right endpoint of the left endpoint of the segment it represents and the interval we want to maintain and
For each point of the left son and right son, the left son inherits the first half [L, (L+R)/2], and the right son inherits half ((L+r)/2,r].
And we maintain the interval and, each large interval is composed of two cells, then the large interval and = left son and + right son and.
This part of the code:
structahah{Long Longl,r,sum,f; For the function of F, there is an explanation behind it, which is ignored here. }tree[200000<<2]; Notice here four times times the space. voidBuildintKintLintR) {TREE[K].L=l;tree[k].r=R; if(tree[k].l==TREE[K].R) {scanf ("%lld",&tree[k].sum); return ; } Long LongMid= (TREE[K].L+TREE[K].R) >>1; Build (k<<1, L,mid); Build (k<<1|1, mid+1, R); Tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;}
Single point query and modification
Single point of modification, we know the position of a single point, then we start from a point, according to the range of two sons represented, choose the next step is to go to the left son or right son, this step to determine the exact point.
A single-point query is almost identical to a single-point modification, and the results are output after the query to a specific location.
Take the picture above to simulate:
Modify 4th points: Left son [0,4], right son [5,8], select left son and left son [0,2], right son [3,4], choose right son->
Query above.
When we have modified a point, the interval that contains the point has changed, so in the end we will add one more sentence:
$tree [k].sum=tree[k \times 2].sum+tree[k \times 2+1].sum$ to ensure the maintenance interval and will not change.
Code: K denotes the number of the point and you need to add Y to the X point.
voidUpdateintk) { if(tree[k].l==TREE[K].R) {Tree[k].sum+=y; return ; } Long LongMid= (TREE[K].L+TREE[K].R) >>1; if(x<=mid) Update (k<<1); ElseUpdate (k<<1|1); Tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;}
Interval summation and modification
The interval modification and the query also have the very big similarity.
Interval modification, for the time being we do not have a good way, only one of the changes in the interval of each element, there will be good practice to explain.
Interval query, we need to identify the interval location of the query.
The following query interval is denoted by [a, b], and K represents the number of the current point.
First we start with the largest interval, judging the range of queries, there are three kinds of situations:
1. In the left son ($b \le tree[k<<1].l $) or the right son ($a > Tree[k<<1|1].l $), then choose the next step is to go to the left son or the right son.
2. The interval to be queried is covered by two parts, then we will divide the interval, part of the query left interval, part of the query right interval.
3. Now the point represents a range of $ (a <= tree[k].l, b >= TREE[K].R) $ is included in the interval to be queried, then do not need to look further, directly add the answer to this section of the maintenance and good.
Take the query interval [3,5] to simulate:
Mid represents the two points of the current interval.
The code is implemented recursively:
voidQueryintk) { if(x<=tree[k].l&&y>=TREE[K].R) {ans+=tree[k].sum; return ; } if(TREE[K].F) down (k);//It's good to omit it first. Long LongMid= (TREE[K].L+TREE[K].R) >>1; if(x<=mid) query (k<<1); if(y>mid) query (k<<1|1);}
Here's the point: Lazy sign
For the interval modification, we waste a lot of time on one of the changes, and modify it is not necessarily to check this point, in order to solve this problem, we introduce lazy Mark F.
First of all, we need to be clear about one of his properties: be lazy, move when you need it, and always be there when you don't.
The lazy tag for each node records the value F added to the interval it represents.
Just like the interval query, when the interval is not included, search separately, when the current interval has been modified by the interval is included, then we can directly to this point, the lazy tag, do not need to go to the exact one of the changes within the range of elements.
So you can't maintain the interval?
We maintain the interval and for what? Of course, in order to find the interval and, when we are in the query, if the use of this entire interval, then return to maintain the value + interval element number $\times$ lazy Tag value, if not fully used to get, then we will be lazy tag to its left and right two sons, and then continue to find. The interval and is not not maintenance, but is in the maintenance of the lazy mark thereby indirectly the maintainer interval and.
It is important to note that when the node's lazy flag is passed to the son, its lazy sign needs to be emptied, because it has been passed on to the son.
Lazy tag Down code:
voidDownLong Longk) {Tree[k<<1].f+=tree[k].f; Tree[k<<1|1].f+=tree[k].f; Tree[k<<1].sum+= (tree[k<<1].r-tree[k<<1].l+1)*tree[k].f; Tree[k<<1|1].sum+= (tree[k<<1|1].r-tree[k<<1|1].l+1)*tree[k].f; TREE[K].F=0;}
Use the interval of the lazy mark and sum it up:
voidQueryintk) { if(x<=tree[k].l&&y>=TREE[K].R) {ans+=tree[k].sum; return ; } if(TREE[K].F) down (k); Long LongMid= (TREE[K].L+TREE[K].R) >>1; if(x<=mid) query (k<<1); if(y>mid) query (k<<1|1);}voidAddLong Longk) { if(tree[k].l>=x&&tree[k].r<=y) {tree[k].sum+ = (tree[k].r-tree[k].l+1)*Val; TREE[K].F+=Val; return ; } if(TREE[K].F) down (k); Long LongMid= (TREE[K].L+TREE[K].R) >>1; if(x<=mid) Add (k<<1); if(y>mid) Add (k<<1|1); Tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;}
The first is the simple line tree operation.
Paste the Template:
#include <cstdio>#include<iostream>using namespacestd;Long LongN,m,ans,x,y,ch,val;structahah{Long Longl,r,sum,f;} tree[200000<<2];voidBuildintKintLintR) {TREE[K].L=l;tree[k].r=R; if(tree[k].l==TREE[K].R) {scanf ("%lld",&tree[k].sum); return ; } Long LongMid= (TREE[K].L+TREE[K].R) >>1; Build (k<<1, L,mid); Build (k<<1|1, mid+1, R); Tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;}voidUpdateintk) { if(tree[k].l==TREE[K].R) {Tree[k].sum+=y; return ; } Long LongMid= (TREE[K].L+TREE[K].R) >>1; if(x<=mid) Update (k<<1); ElseUpdate (k<<1|1); Tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;}voidDownLong Longk) {Tree[k<<1].f+=tree[k].f; Tree[k<<1|1].f+=tree[k].f; Tree[k<<1].sum+= (tree[k<<1].r-tree[k<<1].l+1)*tree[k].f; Tree[k<<1|1].sum+= (tree[k<<1|1].r-tree[k<<1|1].l+1)*tree[k].f; TREE[K].F=0;}voidQueryintk) { if(x<=tree[k].l&&y>=TREE[K].R) {ans+=tree[k].sum; return ; } if(TREE[K].F) down (k); Long LongMid= (TREE[K].L+TREE[K].R) >>1; if(x<=mid) query (k<<1); if(y>mid) query (k<<1|1);}voidAddLong Longk) { if(tree[k].l>=x&&tree[k].r<=y) {tree[k].sum+ = (tree[k].r-tree[k].l+1)*Val; TREE[K].F+=Val; return ; } if(TREE[K].F) down (k); Long LongMid= (TREE[K].L+TREE[K].R) >>1; if(x<=mid) Add (k<<1); if(y>mid) Add (k<<1|1); Tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;}intMain () {scanf ("%lld%lld",&n,&m); Build (1,1, N); for(intI=1; i<=m;i++) {ans=0; CIN>>ch>>x>>y; if(ch==1) {cin>>Val; Add (1); } Else{query (1); cout<<ans<<"\ n"; } }}
Examples:
Entry
Template: Valley Line tree 1:www.luogu.org/problemnew/show/p3372
Single-point modification and interval query: Maximum number www.luogu.org/problemnew/show/P1198
Advanced:
Demon Dream Slash stick: www.luogu.org/problemnew/show/P3797
The Boring series: www.luogu.org/problemnew/show/P1438