线段树 codeforces Alyona and towers 739C

本文详细介绍线段树数据结构的原理与应用,通过具体题目演示如何实现线段树的构建、更新及查询操作,并分享了高效解决问题的思考过程。

今天在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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值