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
#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 ;}