poj 2892 Tunnel Warfare(线段树区间合并)

题目链接

题意:

有 n 个连续的村庄,m 次操作,每次操作分为三个,

  1. D x 表示 x 村庄被摧毁
  2. Q x 表示查询包含 x 村庄的最长连续长度
  3. R 表示修复最近一个被摧毁的村庄

 对于第一个,如果村庄被摧毁,就直接进行单点修改,把该村庄的连续长度变为0,在返回的时候,就将对应区间的左连续和右连续改一下,左连续是左半区间的左连续,右连续是右半区间的右连续,一次向上更新即可

对于第二个,查询的话,如果这个当前区间的左连续包含这个点,输出左连续,如果包含在右连续区间内,输出右连续,如果都没有,看看是否包含在中间的连续区间内,如果包含在内,输出中间的连续区间,如果不在,就根据点的位置去查询左子树或右子树,重复上述过程,如果最后查到了对应的点,就输出点的连续情况即可

对于R操作,用一个栈(模拟栈)去存放被摧毁的信息,每次从栈顶取走一个,恢复即可,恢复的过程与破坏的过程思路一直,只是将连续恢复而已

因为本题目的更新过程只涉及单点更新,所以没必要采用标记下传的方法

#include <iostream>
#include <cstdio>
#include <stack>
#include <vector>
#include <algorithm>
#include <cmath>
#include <stack>
using namespace std;
const int maxn = 50010;
int n, m, kk;
int st[maxn];
struct p{
	int l, r, lsum, rsum, sum;
	int getlen(){
		return r - l + 1;
	}
} c[maxn * 4];
void build(int l, int r, int k){
	c[k].l = l;
	c[k].r = r;
	c[k].lsum = c[k].rsum = c[k].sum = c[k].getlen();
	if(l == r){
		return ;
	}
	int mid = (l + r) / 2;
	build(l, mid, k * 2);
	build(mid + 1, r, k * 2 + 1);
}
void update(int ind, int k, int d){// wrong
	if(c[k].l == c[k].r){
		c[k].lsum = c[k].rsum = c[k].sum = d;
		return;
	}
	int mid = (c[k].l + c[k].r) / 2;
	if(ind <= mid)update(ind, k * 2, d);
	else update(ind, k * 2 + 1, d);
	c[k].lsum = c[k * 2].lsum;
	c[k].rsum = c[k * 2 + 1].rsum;
	if(c[k * 2].lsum ==c[k * 2].getlen())c[k].lsum += c[k * 2 + 1].lsum;
	if(c[k * 2 + 1].rsum == c[k * 2 + 1].getlen())c[k].rsum += c[k * 2].rsum;
	c[k].sum = max(c[k].lsum, max(c[k].rsum, c[k * 2].rsum + c[k * 2 + 1].lsum));
}
int query(int ind, int k){
	if(c[k].l == c[k].r) return c[k].lsum;
	if(c[k].l <= ind && c[k].l + c[k].lsum - 1 >= ind)return c[k].lsum;
	if(c[k].r >= ind && c[k].r - c[k].rsum + 1 <= ind)return c[k].rsum;
	if(c[k * 2].r - c[k * 2].rsum + 1 <= ind && c[k * 2 + 1].l + c[k * 2 + 1].lsum - 1 >= ind)
		return c[k * 2].rsum + c[k * 2 + 1].lsum;
	int mid = (c[k].l + c[k].r) / 2;
	if(ind <= mid)return query(ind, k * 2);
	return query(ind, k * 2 + 1);
}
int main()
{
//	freopen("in.txt", "r", stdin);
	scanf("%d %d", &n, &m);
	build(1, n, 1);
	char op;
	int x;
	kk = 1;
	for (int i = 1; i <= m; i++){
		cin >> op;
		if(op == 'D'){
			scanf("%d", &x);
			update(x, 1, 0);
			st[kk++] = x;
		}
		else if(op == 'Q'){
			scanf("%d", &x);
			printf("%d\n",query(x, 1));
		}
		else if(op == 'R'){
			if(kk >= 1){
				x = st[--kk];
				update(x, 1, 1);
			}
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值