Segment Tree 线段树小结

本文详细介绍线段树的基本概念、代码模板及多种应用场景,包括单点更新区间查询、区间更新区间查询等模型,并提供了多个具体题目的解析。

线段树小结 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. 线段树维护信息举例

题解戳这里

题解戳这里


Blog链接:http://blog.csdn.net/lwt36/article/details/50255129



代码下载链接: https://pan.quark.cn/s/a4b39357ea24 第 一 章 概述 1-1 简述计算机程序设计语言的发展阶段。 解: 自从计算机诞生以来,程序设计语言经历了从机器语言、汇编语言到高级语言的演变过程,C++语言作为一种面向对象的编程语言,也属于高级语言范畴。 1-2 面向对象的编程语言具备哪些特性? 解: 面向对象的编程语言与传统的编程语言有着本质的区别,其设计初衷是为了更直观地模拟现实世界中存在的事物及其相互关系。这类编程语言将客观事物视为具有属性和行为的对象,通过抽象方法提取出同一类对象的共同属性(静态特征)和行为(动态特征),从而构建类。借助类的继承与多态机制,能够便捷地实现代码复用,显著缩短软件开发周期,并确保软件风格的一致性。因此,面向对象的编程语言使得程序能够较为准确地反映问题域的本质,软件开发人员可以运用人类惯用的思维模式进行开发工作。C++语言是目前应用最为广泛的面向对象编程语言。 1-3 结构化程序设计方法是什么?这种方法有哪些优势和不足? 解: 结构化程序设计的核心思想是自顶向下、逐步求精;其程序结构按照功能划分为多个基本模块;各模块之间的关联尽可能简化,在功能上保持相对独立性;每个模块内部均由顺序、选择和循环三种基本结构构成;模块化实现的具体途径是利用子程序。结构化程序设计由于采用模块分解与功能抽象,自顶向下、分而治之的策略,从而有效地将一个较为复杂的程序系统设计任务分解成许多易于管理和处理的子任务,便于开发与维护。 尽管结构化程序设计方法具备诸多优点,但它本质上仍是一种面向过程的程序设计方法,将数据与处理数据的操作分离为相互独立的实体。当数据结构发生变化时,所有相关的处理过程都需要进行相应的调整,每一种...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值