线段树小结

线段树小结 A Summary for Segment Tree

0. Anouncement


Some of the pictures and the content of the text come from the Internet.
Due to plenty of the content,there will be no quotation. If offended, please try to forgive me.

Ⅰ. 线段树

  • 什么是线段树?
    首先我们来看一个图, 线段树就是每个节点维护一个区间的树。
    可以常数倍空间复杂度的情况下高效的 维 护信息(维护一般指的是更新查询) 。
    本文除特别题目外,区间以及根节点下标均从 1 开始,线段树的维护信息均以数组的方法存储。
    线段树

  • 线段树的时空复杂度
    由上图我们可以发现: 线段树维护的区间在同一高度上是完全不相交的,并且恰好包含了所有的区间。
    图中红字所表示的是每个节点的所在的数组的下标
    我们可以发现每个节点的左儿子父亲节点下标 * 2右儿子父亲节点下标 * 2 + 1
    并且我们也发现以这样的存储方式,仅仅开2倍的空间是不够的,4倍才是合适的
    至于为什么是4倍?详情可以看这里,点击查看
    由于线段树的二叉树形结构,更新和查询的时间复杂度操作都是O(logn)

Ⅱ. 线段树的代码模版

  • l,r,rt: 保存的区间信息,其中rt是这个区间信息所存的节点的下标
    L,R: 每次操作要更新的区间
    v: 更新的值
    HH风格的线段树,感谢HH大牛对于线段树学习的分享

  • 单点更新 区间查询(单点查询其实就是区间查询的特例)
    push_up 把儿子节点的信息更新到自己

//以单点更新 区间查询的RMQ为例
const int N = 1e5 + 10;

#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

int maxv[N << 2];

void push_up(int rt) {
    maxv[rt] = max(maxv[rt << 1], maxv[rt << 1 | 1]);
}
void build(int l, int r, int rt) {
    if(l == r) {
        scanf("%d", &maxv[rt]);
        return;
    }
    int m = l + r >> 1;
    build(lson);
    build(rson);
    push_up(rt);
}
void update(int o, int v, int l, int r, int rt) {
    if(l == r) {
        maxv[rt] = v;
        return;
    }
    int m = l + r >> 1;
    if(o <= m) update(o, v, lson);
    else update(o, v, rson);
    push_up(rt);
}
int query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) return maxv[rt];
    int m = l + r >> 1;
    int ret = -INF;
    if(L <= m) ret = max(ret, query(L, R, lson));
    if(R > m) ret = max(ret, query(L, R, rson));
    return ret;
}
  • 区间更新 区间查询
    push_down 在更新或者查询到当前节点的左右儿子节点时,提前更新它的左右儿子节点,
    这就是懒惰操作lazy propagation
    这是一个坎,建议不要去看什么网上的讲解,代码就是最好的讲解,模拟几遍代码就懂了
//以区间更新 区间求和为例
const int N = 1e5 + 10;

#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

int sum[N << 2], add[N << 2];

void push_up(int rt) {
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void push_down(int rt, int m) {
    if(add[rt]) {
        sum[rt << 1] += add[rt] * (m - (m >> 1));
        sum[rt << 1 | 1] += add[rt] * (m >> 1);
        add[rt << 1] += add[rt];
        add[rt << 1 | 1] += add[rt];
        add[rt] = 0;
    }
}
void update(int L, int R, int v, int l, int r, int rt) {
    if(L <= l && r <= R) {
        add[rt] += v;
        sum[rt] += v * (r - l + 1);
        return;
    }
    push_down(rt, r - l + 1);
    int m = l + r >> 1;
    if(L <= m) update(L, R, v, lson);
    if(R > m) update(L, R, v, rson);
    push_up(rt);
}
int query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) return sum[rt];
    push_down(rt, r - l + 1);
    int m = l + r >> 1, ret = 0;
    if(L <= m) ret += query(L, R, lson);
    if(R > m) ret += query(L, R, rson);
    return ret;
}

Ⅲ. 线段树的题目讲解

所有的题目不外乎都是这两种模型,但是题目也有一定的技巧,即使是区间更新也不一定要往下传递lazy标记,
我们将以具体的题目配合进行讲解

1. 单点更新 区间查询

//
//  Created by TaoSama on 2015-05-19
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 5e4 + 10;

#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

int n, sum[N << 2];

void push_up(int rt) {
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}

void build(int l, int r, int rt) {
    if(l == r) {
        scanf("%d", &sum[rt]);
        return;
    }
    int m = l + r >> 1;
    build(lson);
    build(rson);
    push_up(rt);
}

void update(int o, int v, int l, int r, int rt) {
    if(l == r) {
        sum[rt] += v;
        return;
    }
    int m = l + r >> 1;
    if(o <= m) update(o, v, lson);
    else update(o, v, rson);
    push_up(rt);
}

int query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) return sum[rt];
    int m = l + r >> 1;
    int ret = 0;
    if(L <= m) ret += query(L, R, lson);
    if(R > m) ret += query(L, R, rson);
    return ret;
}

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    int kase = 0;
    while(t--) {
        printf("Case %d:\n", ++kase);
        scanf("%d", &n);
        build(root);
        char op[10]; int x, y;
        while(scanf("%s", op)) {
            if(op[0] == 'E') break;
            scanf("%d%d", &x, &y);
            if(op[0] == 'Q') printf("%d\n", query(x, y, root));
            else if(op[0] == 'A') update(x, y, root);
            else if(op[0] == 'S') update(x, -y, root);
        }
    }
    return 0;
}
//
//  Created by TaoSama on 2015-05-19
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 2e5 + 10;

#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
int n, q, maxv[N << 2];

void push_up(int rt) {
    maxv[rt] = max(maxv[rt << 1], maxv[rt << 1 | 1]);
}

void build(int l, int r, int rt) {
    if(l == r) {
        scanf("%d", &maxv[rt]);
        return;
    }
    int m = l + r >> 1;
    build(lson);
    build(rson);
    push_up(rt);
}

void update(int o, int v, int l, int r, int rt) {
    if(l == r) {
        maxv[rt] = v;
        return;
    }
    int m = l + r >> 1;
    if(o <= m) update(o, v, lson);
    else update(o, v, rson);
    push_up(rt);
}

int query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) return maxv[rt];
    int m = l + r >> 1;
    int ret = -INF;
    if(L <= m) ret = max(ret, query(L, R, lson));
    if(R > m) ret = max(ret, query(L, R, rson));
    return ret;
}

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    while(scanf("%d%d", &n, &q) == 2) {
        build(root);
        while(q--) {
            char op[2]; int x, y;
            scanf("%s%d%d", op, &x, &y);
            if(op[0] == 'Q') printf("%d\n", query(x, y, root));
            else update(x, y, root);
        }
    }
    return 0;
}
//
//  Created by TaoSama on 2015-05-21
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 5e4 + 10;

#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
int n, q, maxv[N << 2], minv[N << 2];

void push_up(int rt) {
    maxv[rt] = max(maxv[rt << 1], maxv[rt << 1 | 1]);
    minv[rt] = min(minv[rt << 1], minv[rt << 1 | 1]);
}

void build(int l, int r, int rt) {
    if(l == r) {
        int x; scanf("%d", &x);
        maxv[rt] = minv[rt] = x;
        return;
    }
    int m = l + r >> 1;
    build(lson);
    build(rson);
    push_up(rt);
}

int query(int op, int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) {
        return op ? maxv[rt] : minv[rt];
    }
    int Max = -INF, Min = INF;
    int m = l + r >> 1;
    if(op) {
        if(L <= m) Max = max(Max, query(op, L, R, lson));
        if(R > m) Max = max(Max, query(op, L, R, rson));
    } else {
        if(L <= m) Min = min(Min, query(op, L, R, lson));
        if(R > m) Min = min(Min, query(op, L, R, rson));
    }
    return op ? Max : Min;
}

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    while(scanf("%d%d", &n, &q) == 2) {
        build(root);
        while(q--) {
            int x, y; scanf("%d%d", &x, &y);
            int Max = query(1, x, y, root);
            int Min = query(0, x, y, root);
            printf("%d\n", Max - Min);
        }
    }
    return 0;
}

2. 区间更新 区间查询

//
//  Created by TaoSama on 2015-05-21
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;

#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
int n, q, sum[N << 2], row[N << 2];

void push_up(int rt) {
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}

void push_down(int rt, int m) {
    if(row[rt]) {
        sum[rt << 1] = row[rt] * (m - (m >> 1));
        sum[rt << 1 | 1] = row[rt] * (m >> 1);
        row[rt << 1] = row[rt << 1 | 1] = row[rt];
        row[rt] = 0;
    }
}

void build(int l, int r, int rt) {
    row[rt] = 0;
    if(l == r) {
        sum[rt] = 1;
        return;
    }
    int m = l + r >> 1;
    build(lson);
    build(rson);
    push_up(rt);
}

void update(int L, int R, int v, int l, int r, int rt) {
    if(L <= l && r <= R) {
        sum[rt] = v * (r - l + 1);
        row[rt] = v;
        return;
    }
    push_down(rt, r - l + 1);
    int m = l + r >> 1;
    if(L <= m) update(L, R, v, lson);
    if(R > m) update(L, R, v, rson);
    push_up(rt);
}

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    int kase = 0;
    while(t--) {
        scanf("%d%d", &n, &q);
        build(root);
        while(q--) {
            int x, y, z; scanf("%d%d%d", &x, &y, &z);
            update(x, y, z, root);
        }
        printf("Case %d: The total value of the hook is %d.\n",
               ++kase, sum[1]);
    }
    return 0;
}
//
//  Created by TaoSama on 2015-05-19
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;

#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

typedef long long LL;
int n, q;
LL sum[N << 2], add[N << 2];

void push_up(int rt) {
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}

void push_down(int rt, int m) {
    if(add[rt]) {
        add[rt << 1] += add[rt];
        add[rt << 1 | 1] += add[rt];
        sum[rt << 1] += add[rt] * (m - (m >> 1));
        sum[rt << 1 | 1] += add[rt] * (m >> 1);
        add[rt] = 0;
    }
}

void build(int l, int r, int rt) {
    add[rt] = 0;
    if(l == r) {
        scanf("%lld", &sum[rt]);
        return;
    }
    int m = l + r >> 1;
    build(lson);
    build(rson);
    push_up(rt);
}

void update(int L, int R, int v, int l, int r, int rt) {
    if(L <= l && r <= R) {
        add[rt] += v;
        sum[rt] += (LL)v * (r - l + 1);
        return;
    }
    push_down(rt, r - l  + 1);
    int m = l + r >> 1;
    if(L <= m) update(L, R, v, lson);
    if(R > m)update(L, R, v, rson);
    push_up(rt);
}

LL query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) return sum[rt];
    push_down(rt, r - l + 1);
    int m = l + r >> 1;
    LL ret = 0;
    if(L <= m) ret += query(L, R, lson);
    if(R > m) ret += query(L, R, rson);
    return ret;
}

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    while(scanf("%d%d", &n, &q) == 2) {
        build(root);
        while(q--) {
            char op[2]; int x, y, z;
            scanf("%s%d%d", op, &x, &y);
            if(op[0] == 'Q') printf("%lld\n", query(x, y, root));
            else {
                scanf("%d", &z);
                update(x, y, z, root);
            }
        }
    }
    return 0;
}

题解戳这里

题解戳这里

题解戳这里

3. 线段树区间合并

题解戳这里

题解戳这里

题解戳这里

4. dfs序维护区间线段树

题解戳这里

5. 扫描线与线段树

  • HDU 1542 Atlantis
    矩形面积并 不用传递懒惰标记 模拟一下就懂了
    update:区间更新

  • POJ 1177 Picture
    矩形周长并 与上面那个题相似 两种做法
    第一种直接横着扫描一次再竖着扫描一次
    第二种在横着扫描的时候再同时维护 竖线的个数
    update:单点增减 query:区间求和

  • HDU 1255 覆盖的面积
    前面的与矩形面积并类似, 不同的是push_up的时候要考虑至少覆盖一次one和至少覆盖两次two的更新
    update:区间更新

扫描线与线段树经典三题,全部详细题解戳这里

6. 线段树求第K大(其实就是在线段树上二分)

题解戳这里

题解戳这里

7. 高维线段树与多颗线段树

题解戳这里

题解戳这里

8. 线段树维护信息举例

题解戳这里

题解戳这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值