HDU1540 Tunnel Warfare (线段树区间合并)

题字:有n个连续村庄D代表破坏村庄,R代表修复一个被破坏的村庄,Q代表询问包括x在内的最大连续村庄是的长度

思路:在线段树的区间内,我们要用三个变量记录l开始的连续区间长度r截至的连续区间长度lr区间内最大连续区间长度

更新时最大区间长度等于左右区间的最大连续区间的最大值与右子树的左连续区间+左子树的右连续区间的最大值

查询时注意查询节点左右区间分界处的连续区间内

 

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
#define N 1000100
struct node
{
	LL l;
	LL r;
	LL lm;  // l开始的 连续区间长度
	LL rm;  // r截至的 连续区间长度
	LL mm;  // l-r 区间内最大连续区间长度
}tree[N << 2];
LL used[N];  //模拟栈
inline LL max(LL a, LL b, LL c)
{
	a = a > c ? a : c;
	return a > b ? a : b;
}
void build(LL root, LL l, LL r)
{
	tree[root].l = l;
	tree[root].r = r;
	tree[root].lm = r - l + 1;   //初始化村庄都是好的 所以 右、左、最大连续区间长度为区间长度
	tree[root].rm = r - l + 1;
	tree[root].mm = r - l + 1;
	if (l == r) { return; }
	LL mid = (l + r) / 2;
	build(root * 2, l, mid);
	build(root * 2 + 1, mid + 1, r);
}
void update(LL root, LL pos, LL val)
{
	if (tree[root].l == tree[root].r)
	{
		tree[root].lm = val;
		tree[root].rm = val;
		tree[root].mm = val;
		return;
	}
	int mid = (tree[root].l + tree[root].r) / 2;
	if (pos <= mid)  
	{
		update(root * 2, pos, val);
	}
	if (pos > mid)
	{
		update(root * 2 + 1, pos, val);
	}
	tree[root].lm = tree[root * 2].lm;
	tree[root].rm = tree[root * 2 + 1].rm;
	tree[root].mm = max(tree[root * 2].mm, tree[root * 2 + 1].mm, tree[root * 2].rm + tree[root * 2 + 1].lm); //最大区间长度等于 左右区间的最大连续区间的最大值 与 右子树的左连续区间+左子树的右连续区间 的最大值
	if (tree[root * 2].lm == (tree[root * 2].r - tree[root * 2].l + 1)) //假如右子树的区间是满的话 那么更新后父亲节点可以延长到左子树的右连续区间
	{
		tree[root].lm += tree[root * 2 + 1].lm;
	}
	if (tree[root * 2 + 1].rm == (tree[root * 2 + 1].r - tree[root * 2 + 1].l + 1)) //同理
	{
		tree[root].rm += tree[root * 2].rm;
	}
}
LL query(LL root, LL pos)
{
	if (tree[root].mm == 0 || tree[root].l == tree[root].r || tree[root].mm == (tree[root].r - tree[root].l + 1)) //  叶子节点返回 (区间全空、全满返回 优化时间)
	{
		return tree[root].mm;
	}
	int mid = (tree[root].l + tree[root].r) / 2;
	if (pos <= mid)
	{
		if (pos >= (tree[root * 2].r - tree[root * 2].rm + 1))  //判断pos位置是否在右子树的左连续区间上  tree[root * 2].r - tree[root * 2].rm + 1 代表右子树的左连续区间开始位置  如果是则 左子树的右连续区间可以增加到ans上 
		{
			return query(root * 2, pos) + query(root * 2 + 1, mid + 1);
		}
		else
		{
			return query(root * 2, pos);
		}
	}
	if (pos > mid)  
	{
		if (pos <= (tree[root * 2 + 1].l + tree[root * 2 + 1].lm - 1))  //同理
		{
			return query(root * 2 + 1, pos) + query(root * 2, mid);
		}
		else
		{
			return query(root * 2 + 1, pos);
		}
	}
}
int main()
{
	LL n, m, i, x, y, k;  //k为栈的指针
	char f[10];
	while (scanf("%lld%lld", &n, &m) != EOF)
	{
		build(1, 1, n);
		k = 1;
		memset(used, 0, sizeof(used));
		while (m--)
		{
			scanf("%s", f);
			if (f[0] == 'D')
			{
				scanf("%lld", &x);
				used[k++] = x;
				update(1, x, 0);
			}
			if (f[0] == 'R')
			{
				x = used[--k];
				update(1, x, 1);
			}
			if (f[0] == 'Q')
			{
				scanf("%lld", &y);
				printf("%lld\n", query(1, y));
			}
		}
	}
	return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值