今天在CF上面看到一个题目,是有关线段树的。好久没有写过线段树,基本都已经忘完。没办法,后来只得看了网上的题解。写下这篇博客,总结一下线段树的操作,便于以后学习,复习。线段树主要是用来维护区间信息,快速的更新与查询的数据结构,在运用过程中,需要根据具体情况,判断出需要维护的信息,达到快速查询,更新的效果。与它的功能对应,线段树的操作主要是更新和查询,当然还有建树(建树类类似于更新)对应的代码中,也体现为三个函数(本题涉及到的查询比较单一,所以没有写成函数)。当然,对于这三个函数,最重要的还是考虑线段树的如何合并问题,所以,通常需要将合并单独写成一个函数。对于线段树的题目,最主要的是,为了得到最终的结果,需要判断出线段树的节点需要维护哪些信息。对比这个题目,其实应该是有一个思考的过程: 我们的目的是什么——》当只有一个节点时,问题的解是什么——》对于两个小的区间,如何去合并得到大区间的解(通常需要考虑多种情况,主要是思考合并时的边界如何处理)——》我们需要维护什么来保证合并后得到正确的大区间的解。
这个题目,为了得到合并后的大区间的解,我们需要维护:区间的答案,区间最左边的答案,区间最右边的答案,区间左边严格递减的答案,区间右边严格递增的答案,区间长度等信息。这个题目还是很难想的,首先需要将区间更新转化为点更新。然后合并的时候需要维护区间的6个信息,才能正确合并!贴上我的代码:
(提示:记录线段树信息的树组的长度通常为节点长度的4倍)
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll __int64
using namespace std;
const int MAX = 300001;
ll a[MAX], b[MAX];
struct Node{
int l, r, len, pre, las, lans, rans, ans;
}p[MAX*4];
Node merge(int x, int y){
Node u = p[x], v = p[y];
Node tmp;
tmp.l = u.l;
tmp.r = v.r;
tmp.len = u.len + v.len;
tmp.pre = u.pre;
if (u.pre == u.len){
tmp.pre += v.pre;
}
tmp.las = v.las;
if (v.las == v.len){
tmp.las += u.las;
}
tmp.lans = u.lans;
if (u.lans == u.len){
if (u.las == u.len){
tmp.lans += max(v.pre, v.lans);
}else{
tmp.lans += v.pre;
}
}
tmp.rans = v.rans;
if (v.rans == v.len){
if (v.pre == v.len){
tmp.rans += max(u.las, u.rans);
}else{
tmp.rans += u.las;
}
}
tmp.ans = max(u.ans, v.ans);
tmp.ans = max(tmp.ans, tmp.lans);
tmp.ans = max(tmp.ans, tmp.rans);
tmp.ans = max(tmp.ans, u.rans + (u.las ? max(v.pre, v.lans) : v.pre));
tmp.ans = max(tmp.ans, v.lans + (v.pre ? max(u.las, u.rans) : u.las));
return tmp;
}
void build(int k, int x, int y){
if (x == y){
p[k].l = x;
p[k].r = y;
p[k].pre = p[k].las = p[k].lans = p[k].rans = p[k].ans = 0;
p[k].len = 1;
if (b[x] < 0) p[k].pre = 1;
if (b[x] > 0) p[k].las = 1;
if (b[x] != 0){
p[k].ans = p[k].lans = p[k].rans = 1;
}
return;
}
int mid = (x + y) / 2;
build(k*2, x, mid);
build(k*2+1, mid+1, y);
p[k] = merge(k*2, k*2+1);
}
//int l, r, len, pre, las, lans, rans, ans;
void modify(int k, int l, int r, int x, ll d){
//printf("%d %d %d %d %d\n", k, l, r, x, d);
if (l == x && r == x){
b[x] += d;
p[k].pre = p[k].las = p[k].lans = p[k].rans = p[k].ans = 0;
p[k].len = 1;
if (b[x] < 0){
p[k].pre = 1;
}
if (b[x] > 0){
p[k].las = 1;
}
if (b[x] != 0){
p[k].lans = p[k].rans = p[k].ans = 1;
}
return ;
}
int mid = (l + r) / 2;
if (x <= mid){
modify(k*2, l, mid, x, d);
}else{
modify(k*2+1, mid+1, r, x, d);
}
p[k] = merge(k*2, k*2+1);
}
int main(int argc, char const *argv[]){
/* code */
int n, m;
while (scanf("%d", &n) != EOF){
for (int i = 1; i<=n; i++) scanf("%I64d", &a[i]);
a[0] = a[1];
for (int i = 1; i<=n; i++) b[i] = a[i] - a[i-1];
build(1, 1, n);
scanf("%d", &m);
int x, y, z;
for (int i = 0; i<m; i++){
scanf("%d%d%d", &x, &y, &z);
if (x > 1){
modify(1, 1, n, x, z);
}
if (y < n){
modify(1, 1, n, y+1, -z);
}
printf("%d\n", p[1].ans + 1);
}
}
return 0;
}
本文详细介绍线段树数据结构的原理与应用,通过具体题目演示如何实现线段树的构建、更新及查询操作,并分享了高效解决问题的思考过程。

被折叠的 条评论
为什么被折叠?



