[数据结构]线段树

线段树:这么高大上的名字以前从来不敢想23333.

线段树主要用于一段区间,其中包括 单点修改、区间修改、单点查询、区间查询,其中可能查询最大值或者一段区间的和。

它的本质把一段区间划分成一小段一小段。线段树的时间复杂度为O(logn)

我在想为什么单点是O(n),因为还是空间换时间了。数组不仅记录了单点的值,还记录了一个大段的值。

在查询的过程中,就不必一个一个点去查询,这样就会快。所以线段树也是一个不稳定的东西。

刚开始建树。一个一个输入叶子,在更新根的值。

 1 void push_up(int rt) {
 2     tree[rt] = tree[rt<<1] + tree[rt<<1|1];
 3 }
 4 void build(int rt, int l, int r) { // rt节点编号  l是节点维护的区间的左端点 r右端点 
 5     if(l == r) {
 6         scanf("%d", &a[l]);
 7         tree[rt] = a[l];
 8         return ;
 9     }
10     int m = l+r >> 1;
11     build(rt<<1, l, m);
12     build(rt<<1|1, m+1, r);
13     push_up(rt);
14 }

单点修改:

找到这个点的位置,单点增加。全部更新根的值

void update(int p, int x, int rt, int l, int r) {
    if(l == r) {
        tree[rt] += x;
        return ;
    }    
    int m = l+r >> 1;
    if(p <= m) update(p, x, rt<<1, l, m);
    else update(p, x, rt<<1|1, m+1, r);
    push_up(rt);
} 

区间修改:

需要用一个lazy标记。

如果全包围,根的值要更新,lazy标记上。

如果不是全部包围,那么就要往下更新值,往下放。

往下放的意思就是 往下面小的区间去更新,把当前的lazy删除,加到下面两个区间的lazy,下面两个区间更新根的值。

最后再去小区间看一看。

一直递归到全包围为止。

 1 void push_down(int rt, int len) {
 2     lazy[rt<<1] += lazy[rt];
 3     lazy[rt<<1|1] += lazy[rt];
 4     tree[rt<<1] += (len - (len>>1)) * lazy[rt];
 5     tree[rt<<1|1] += (len>>1) * lazy[rt];
 6     lazy[rt] = 0;
 7 }
 8 void Update(int L, int R, int x, int rt, int l, int r) {
 9     if(L <= l && r <= R) {
10         tree[rt] += (r-l+1)*x;
11         lazy[rt] += x;
12         return ;
13     }
14     if(lazy[rt]) push_down(rt, r-l+1);
15     int m = l+r >> 1;
16     if(L <= m) Update(L, R, x, rt<<1, l, m);
17     if(R >m) Update(L, R, x, rt<<1|1, m+1, r);
18 }

最后的最后查询啦

如果全包围,就直接输出。

不是全包围,有lazy就要往下放。

再往下找。

1 int query(int L, int R, int rt, int l, int r) {
2     if(L <= l && r <= R) return tree[rt];
3     int m = l+r >> 1;
4     int ans = 0;
5     if(lazy[rt]) push_down(rt, r-l+1);
6     if(L <= m) ans += query(L, R, rt<<1, l, m);
7     if(R > m) ans += query(L, R, rt<<1|1, m+1, r);
8     return ans;    
9 }

 

posted @ 2019-01-21 15:12  kaike  阅读(142)  评论(0编辑  收藏  举报