线段树小结

线段树小结 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 开始,线段树的维护信息均以数组的方法存储。
    线段树小结_第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. 单点更新 区间查询

  • HDU 1166 敌兵布阵
    update:单点增减 query:区间求和
//
//  Created by TaoSama on 2015-05-19
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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;
}
  • HDU 1754 I Hate It
    update:单点更新 query:区间最值
//
//  Created by TaoSama on 2015-05-19
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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;
}
  • POJ 3264 Balanced Lineup
    query:区间最值
//
//  Created by TaoSama on 2015-05-21
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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. 区间更新 区间查询

  • HDU 1698 Just a Hook
    update:区间更新
//
//  Created by TaoSama on 2015-05-21
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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;
}
  • POJ 3486 A Simple Problem with Integers
    update:区间更新 query:区间求和
//
//  Created by TaoSama on 2015-05-19
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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;
}
  • POJ 2528 Mayor’s posters
    update:区间更新 query:查询所有节点

题解戳这里

  • HDU 4027 Can you answer these queries?
    Tip: 多次开方后会变成1
    update:区间更新 query:区间查询

题解戳这里

  • ZOJ 1610 Count the Colors
    update:区间染色(技巧) query:单点查询

题解戳这里

3. 线段树区间合并

  • HDU 1540 Tunnel Warfare
    这类题目会询问区间中满足条件的连续最长区间,经典的维护技巧

题解戳这里

  • POJ 3667 Hotel

题解戳这里

  • HDU 3308 LCIS

题解戳这里

4. dfs序维护区间线段树

  • HDU 3974 Assign the task
    维护树的区间 可以使用dfs时间戳
    update:区间更新 query:单点查询

题解戳这里

5. 扫描线与线段树

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

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

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

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

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

  • POJ 2886 Who Gets the Most Candies?

题解戳这里

  • HDU 5592 BestCoder Round #65 C. ZYB’s Premutation

题解戳这里

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

  • Uva 11992 Fast Matrix Operations
    二维线段树,一道非常好的复习线段树的题目,包含了线段树所有基本操作

题解戳这里

  • HDU 4267 A Simple Problem with Integers
    多颗线段树,将零散的偏移量由线段树变为连续的

题解戳这里

8. 线段树维护信息举例

  • POJ 3255 Help with Intervals
    线段树与集合操作

题解戳这里

  • Codeforces #321 E. Kefa and Watch
    线段树维护字符串哈希值

题解戳这里

你可能感兴趣的:(学习小结,数据结构,-,线段树)