码迷,mamicode.com
首页 > 其他好文 > 详细

线段树详解

时间:2018-06-10 18:59:25      阅读:176      评论:0      收藏:0      [点我收藏+]

标签:区间更新   return   The   das   访问   gravity   连续   子节点   区间查询   

一:定义

首先要明确线段树的定义,线段树是一颗树,而且是完全二叉树。同时线段树的每个节点表示一个区间,左子树和右子树分别表示这个区间的左半边和右半边。

即将区间[L,R]分解成[L,MID]和[MID+1,R],假设根的高度为1,树高为技术分享图片(n>1)

下图展示了区间[1,13]的分解过程

技术分享图片

二:原理

上图中每个节点存储自己对应区间的信息。

(1)单点修改

假设要修改1号节点,不难发现只要修改[1,13]、[1,7]、[1,4]、[1,2]、[1,1]这几个节点的信息,也就是更新1号节点到根节点的这条链上的所有信息

所以最大修改次数就是树的高度技术分享图片

(2)区间查询

我们可以将一个[1,n]的线段树分成若干个连续不相交区间,嗯大概是技术分享图片

详细证明可以参考https://www.cnblogs.com/AC-King/p/7789013.html

(3)区间修改

朴素算法:把区间内的每个点单点修改(好像有那么一点点...暴力)

那该怎么办呢,这时候一个神奇的东西出现了——懒惰标记(Lazy tag)

我们把对节点的修改情况储存在标记里,在访问一个节点的时候,“顺便”把它的标记传递给它的儿子节点,也就是懒惰标记的下放。

实现思路(重点)

<1>增加一个新的变量用来存储Lazy tag

<2>递归到这个节点时,只更新这个节点的状态,并把当前的更改值累积到标记里

<3>当需要递归这个节点的子节点的时候,标记下传给子节点

  ①当前节点的懒惰标记累积到子节点的懒惰标记中

  ②修改子节点状态(原状态+子节点区间大小*父节点传下来的懒惰标记)

  ③父节点懒惰标记清零

三:代码实现

 

 1 const int MAXN=50010;
 2 int a[MAXN],ans[MAXN<<2],lazy[MAXN<<2];
 3 //a[]为原序列信息,ans[]模拟线段树维护区间和,lazy[]为懒惰标记
 4 void PushUp(int rt)
 5 {
 6     ans[rt]=ans[rt<<1]+ans[rt<<1|1];
 7 }
 8 void Build(int l,int r,int rt)
 9 {
10     if (l==r)
11     {
12         ans[rt]=a[l];
13         return;
14     }
15     int mid=(l+r)>>1;
16     Build(l,mid,rt<<1);
17     Build(mid+1,r,rt<<1|1);
18     PushUp(rt);
19 }
20 void PushDown(int rt,int ln,int rn)//ln表示左子树元素结点个数,rn表示右子树结点个数
21 {
22     if (lazy[rt])
23     {
24         lazy[rt<<1]+=lazy[rt];
25         lazy[rt<<1|1]+=lazy[rt];
26         ans[rt<<1]+=lazy[rt]*ln;
27         ans[rt<<1|1]+=lazy[rt]*rn;
28         lazy[rt]=0;
29     }
30 }
31 void Add(int L,int C,int l,int r,int rt)
32 {
33     if (l==r)
34     {
35         ans[rt]+=C;
36         return;
37     }
38     int mid=(l+r)>>1;
39     //PushDown(rt,mid-l+1,r-mid); 若既有点更新又有区间更新,需要这句话
40     if (L<=mid)
41         Add(L,C,l,mid,rt<<1);
42     else
43         Add(L,C,mid+1,r,rt<<1|1);
44     PushUp(rt);
45 }
46 void Update(int L,int R,int C,int l,int r,int rt)
47 {
48     if (L<=l&&r<=R)
49     {
50         ans[rt]+=C*(r-l+1);
51         lazy[rt]+=C;
52         return;
53     }
54     int mid=(l+r)>>1;
55     PushDown(rt,mid-l+1,r-mid);
56     if (L<=mid) Update(L,R,C,l,mid,rt<<1);
57     if (R>mid) Update(L,R,C,mid+1,r,rt<<1|1);
58     PushUp(rt);
59 }
60 LL Query(int L,int R,int l,int r,int rt)
61 {
62     if (L<=l&&r<=R)
63         return ans[rt];
64     int mid=(l+r)>>1;
65     PushDown(rt,mid-l+1,r-mid);//若更新只有点更新,不需要这句
66     LL ANS=0;
67     if (L<=mid) ANS+=Query(L,R,l,mid,rt<<1);
68     if (R>mid) ANS+=Query(L,R,mid+1,r,rt<<1|1);
69     return ANS;
70 }
71 int main()
72 {
73     //建树   
74     Build(1,n,1);   
75     //点更新  
76     Add(L,C,1,n,1);  
77     //区间修改   
78     Update(L,R,C,1,n,1);  
79     //区间查询   
80     int ANS=Query(L,R,1,n,1);  
81 }

 

线段树详解

标签:区间更新   return   The   das   访问   gravity   连续   子节点   区间查询   

原文地址:https://www.cnblogs.com/kousak/p/9163713.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!