--线段树--
线段树 (Segment Tree) ,一个用来处理 RMQ、RSQ 的数据结构
1. 基本思想
它的思想也是非常简单,比如有一个长度为 8 的区间,我们把它分成两个长度为 4 的区间,然后对于长度为 4 的区间再分割,每个区间记录信息(比如和) ,我们把这些区间当成一棵树上的节点,对于编号为 k k k 的区间 [ l , r ] [l,r] [l,r] ,中间点为 m i d = ⌊ l + r 2 ⌋ mid=\lfloor \dfrac{l+r}{2} \rfloor mid=⌊2l+r⌋ ,子节点有编号为 2 k 2k 2k 的区间 [ l , m i d ] [l,mid] [l,mid] 和编号为 2 k + 1 2k+1 2k+1 的区间 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] ,若 s [ k ] s[k] s[k] 代表编号为 k k k 的区间和,那么显然 s [ k ] = s [ 2 k ] + s [ 2 k + 1 ] s[k]=s[2k]+s[2k+1] s[k]=s[2k]+s[2k+1] ,非常的简单
(为了图方便,我写了俩内联函数:
inline int ls(int x){return x<<1;}//左孩子
inline int rs(int x){return x<<1|1;}//右孩子
2. 单点修改区间查询
例题
求区间和,还是 s [ k ] s[k] s[k] 代表编号为 k k k 的区间和,那么首先第一步是初始化 s [ ] s[] s[]
void build(int k, int l, int r)
{//编号k区间对应[l,r] if(l==r){s[k]=a[l];return;}int mid=l+r>>1;build(ls(k), l, mid);build(rs(k), mid+1, r);s[k]=s[ls(k)]+s[rs(k)];
}//注意s的大小应至少是a的四倍
单点修改把包括某个点的区间都加上就行
void add(int k, int l, int r, int x, int v)
{//计算到编号k的区间[l,r],要把x元素加上vif(r<x || l>x)return;if(l==r && l==x){s[k]+=v;return;}int mid=l+r>>1;add(2*k, l, mid, x, v);add(2*k+1, mid+1, r, x, v);s[k]=s[2*k]+s[2*k+1];
}
区间查询:
int query(int k, int l, int r, int x, int y)
{//k,l,r同上,求sum[x,y]if(r<x || l>y)return 0;if(x<=l && r<=y)return s[k];int mid=l+r>>1;return query(2*k, l, mid, x, y)+query(2*k+1, mid+1, r, x, y);
}
于是 AC 了
3. 区间修改单点查询
例题
区间修改很简单,我们把每一个区间都打一个 add 标记,表示这个区间每一个数都加上了 add ,这样单点查询只需要从根开始一层一层找包括它的区间,把 add 标记都加上就行了
4. 区间修改区间查询
例题
4.1 标记下传
如果区间修改区间查询也用直接打 add 标记的办法,效率会比朴素算法还要差,为什么呢?因为我们对于每个点都去找包含它的区间,每一个点都是独立的,那我们把每个标记都下传到子节点不就行了?但是我们发现每一次修改都下传时间复杂度会很大,其实我们不用每次修改都下传,在查询的时候,修改的时候,顺便把路过的节点都下传了就行,因为这样的标记只有路过的时候才会下传,于是我们管他叫 lazy-tag (懒标记),代码:
#include
#include
#include
#include
#define rp(i, e) for(int i=1;i<=(e);i++)
#define pr(i, e) for(int i=(e);i>=1;i--)
#define rp0(i, e) for(int i=0;i<(e);i++)
#define pr0(i, e) for(int i=(e)-1;i>=0;i--)
#define rps(i, b, e) for(int i=(b);i<=(e);i++)
#define prs(i, e, b) for(int i=(e);i>=(b);i--)
#define rpg(i, x) for(int i=head[x];i;i=e[i].nxt)
using namespace std;
const int NR=1e5+10;
typedef long long LL;
int n, T, o, x, y;
LL a[NR], k;
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
struct segTree
{LL s[NR*4], ad[NR*4];void build(int k, int l, int r){if(l==r){s[k]=a[l];return;}int mid=l+r>>1;build(ls(k), l, mid);build(rs(k), mid+1, r);s[k]=s[ls(k)]+s[rs(k)];}void addrange(int k, int l, int r, LL v){ad[k]+=v;s[k]+=(r-l+1)*v;}void pushdown(int k, int l, int r){if(!ad[k])return;int mid=l+r>>1;addrange(ls(k), l, mid, ad[k]);addrange(rs(k), mid+1, r, ad[k]);ad[k]=0;}void add(int k, int l, int r, int x, int y, LL v)
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
