HDU_1540_线段树区间连续维护

HDU 1540

  • 求包括该点在内左右边的连续长度
  • 线段树维护3个值.
    len:该点的区间中最大连续长度.
    leftlen:该点区间左端的最大连续长度
    rightlen:该点区间右端的最大连续长度

线段树区间连续长度的维护:

  • 父节点的区间连续长度左子树区间长度、右子树区间长度、左子树右端+右子树左端的区间长度3着的中最大值
  • 父节点左端连续长度=左子树左端连续长度.如果左子树区间连续,那么还要加上右子树左端的区间连续长度
  • 同理.父节点右端连续长度=右子树右端连续长度.如果右子树区间连续,那么还要加上左子树的右端区间连续长度

查询某个点index的区间连续长度

  • 如果该点的区间恰好连续,或者区间连续长度=0,直接返回
  • 如果index左子树右端连续区间内,那么返回右端连续区间长度+右子树左端连续区间长度,不在就缩小范围去左子树继续递归找
  • indexmid右边也是同理.看代码把.
#include <iostream>
#include <algorithm>
#include <stack>
using namespace std;
const int maxn = 5e4+5;
int n,m;
int num[maxn];
struct segement {
	int len, leftlen, rightlen;
	#define len(x) tree[x].len
	#define leftlen(x) tree[x].leftlen
	#define rightlen(x) tree[x].rightlen
}tree[4*maxn];
void pushUp(int p,int left,int right) {
	len(p) = max(len(2*p),len(2*p+1)); //左子树和右子树长度最大值
	len(p) = max(len(p),rightlen(2*p)+leftlen(2*p+1)); // 或者是左子树右边连续+右子树左边连续
	leftlen(p) = leftlen(2 * p); //父亲节点的最大左边长度是左子节点左边长度
	rightlen(p) = rightlen(2 * p + 1);
	int mid = (left + right)/2;
	// 如果左子树是连续的,那么父节点左子树的值=左子树+右子树左端长度
	if (leftlen(p) == mid - left + 1) leftlen(p) += leftlen(2*p+1);
	if (rightlen(p) == right - mid) rightlen(p) += rightlen(2*p);
}
void build(int p,int left,int right) {
	if (left == right) {
		len(p) = leftlen(p) = rightlen(p) = 1;
		return;
	}
	int mid = (left + right) / 2;
	build(2*p,left,mid);
	build(2*p+1,mid+1,right);
	pushUp(p,left,right);
}
void update(int p,int left,int right,int index,int val) {
	if (left == right && right == index) { //找到叶节点
		len(p) = leftlen(p) = rightlen(p) = val;
		return;
	}
	int mid = (left + right)/2;
	if (index <= mid) update(2*p,left,mid,index,val);
	else update(2*p+1,mid+1,right,index,val);
	pushUp(p,left,right);
}
int query(int p,int left,int right,int index) {
	if (len(p) == 0 || len(p) == right - left + 1) return  len(p);
	int mid = (left + right) / 2;
	if (index <= mid) 
		// index不在左子树最右连续区间内,继续递归查找,反之同理
		return index > mid - rightlen(2*p) ? rightlen(2*p) + leftlen(2*p+1) : query(2*p,left,mid,index);
	return index <= mid + leftlen(2*p+1) ? leftlen(2*p+1) + rightlen(2*p) : query(2*p+1,mid+1,right,index);
}
int main() {
//	freopen("a.txt", "r", stdin);
	char ch[5];
	int p;
	while (scanf("%d%d",&n,&m) != EOF) {
		stack<int> s;
		build(1, 1, n);
		while (m--) {
			scanf("%s",ch);
			if (ch[0] == 'R' && !s.empty()) {
				p = s.top(); s.pop();
				update(1,1,n,p,1);
			} else if (ch[0] == 'D') {
				scanf("%d",&p);
				update(1, 1, n, p, 0);
				s.push(p);
			} 
			else {
				scanf("%d",&p);
				printf("%d\n",query(1,1,n,p));
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值