线段树小结 A Summary for Segment Tree
本文部分图片以及部分内容来自互联网,内容过多就不一一注明出处了,冒犯之处还请海涵。
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;
}
//以区间更新 区间求和为例
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标记,
我们将以具体的题目配合进行讲解
//
// 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;
}
//
// 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;
}
//
// 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;
}
//
// 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;
}
//
// 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;
}
题解戳这里
题解戳这里
题解戳这里
题解戳这里
题解戳这里
题解戳这里
题解戳这里
HDU 1542 Atlantis
矩形面积并 不用传递懒惰标记 模拟一下就懂了
update:区间更新
POJ 1177 Picture
矩形周长并 与上面那个题相似 两种做法
第一种直接横着扫描一次再竖着扫描一次
第二种在横着扫描的时候再同时维护 竖线的个数
update:单点增减 query:区间求和
HDU 1255 覆盖的面积
前面的与矩形面积并类似, 不同的是push_up的时候要考虑至少覆盖一次one和至少覆盖两次two的更新
update:区间更新
扫描线与线段树经典三题,全部详细题解戳这里
题解戳这里
题解戳这里
题解戳这里
题解戳这里
题解戳这里
题解戳这里