【Hdu】1540 Tunnel Warfare(线段树|区间合并)

题目大意:有N个村子排成一条直线,每个村子都连接了它的左右两个村子(除了最左边和最右边的外),有3种操作,(1)"D x",表示将第x个村子摧毁。(2)"Qx",表示查询与第x个村子直接和间接相连的村子有多少个。(3)"R",表示将上一次摧毁的村子复原。

这边有一个需要注意的点是村子可以多次被摧毁,但只需要一次便可恢复。

思路:

这边我只用两个数组lsum,和rsum

其中作用分别是表示左边界向右连续的最大长度,右边界向左连续的最大长度

然后先询问的函数query中,所采用的是不断缩放区间,直到最后成为一个点,并且缩放后区间的lsum和rsum的意义也发生改变

lsum表示的是左边界向左连续的最大长度,rum表示右边界向右连续的最大长度,这样最后对于询问的点q,lsum表示的就是不包括该点向左连续的最大长度,rsum表示的就是不包括该点向右连续的最大长度。

有了这个思想基础之后,剩下的便是转化了,搞了好久。详细看代码吧。

#define  _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<stack>
using namespace std;
#define MAX 50005
#define ls rt<<1
#define rs ls|1
#define m (l+r)>>1

int lsum[MAX << 2];
int rsum[MAX << 2];
int bb[MAX];
int ans;

void uprt(int len, int rt)
{
	lsum[rt] = lsum[ls];
	rsum[rt] = rsum[rs];
	if (lsum[rt] == len - (len >> 1))
		lsum[rt] += lsum[rs];
	if (rsum[rt] == (len >> 1))
		rsum[rt] += rsum[ls];
}

void updata(int q, int c, int l, int r, int rt)
{
	if (l == r)
	{
		lsum[rt] = rsum[rt] = c;
		bb[l] = c;
		return;
	}
	int mid = m;
	if (q <= mid)
		updata(q, c, l, mid, ls);
	else
		updata(q, c, mid + 1, r, rs);
	uprt(r - l + 1, rt);
}

void query(int q, int l, int r, int rt)
{
	if (l == r)
	{
		ans = (lsum[rt] + rsum[rt])*bb[l] + bb[l];
		return;
	}
	int mid = m;
	int len = r - l + 1;
	int a, b;
	if (q <= mid)
	{
		a = rsum[ls];
		b = lsum[ls];
		if (rsum[ls] != 0)
			rsum[ls] = lsum[rs];
		if (lsum[rs] == (len >> 1))
			rsum[ls] += rsum[rt];
		lsum[ls] = lsum[rt];
		query(q, l, mid, ls);
		rsum[ls] = a;
		lsum[ls] = b;
	}
	else
	{
		a = rsum[rs];
		b = lsum[rs];
		if (lsum[rs] != 0)
			lsum[rs] = rsum[ls];
		if (rsum[ls] == len - (len >> 1))
			lsum[rs] += lsum[rt];
		rsum[rs] = rsum[rt];
		query(q, mid + 1, r, rs);
		rsum[rs] = a;
		lsum[rs] = b;
	}
}

void reset(int l, int r, int rt)
{
	if (l == r)
		return;
	int mid = m;
	reset(l, mid, ls);
	reset(mid + 1, r, rs);
	uprt(r - l + 1, rt);
}

void build(int l, int r, int rt)
{
	if (l == r)
	{
		rsum[rt] = lsum[rt] = 1;
		bb[l] = 1;
		return;
	}
	int mid = m;
	build(l, mid, ls);
	build(mid + 1, r, rs);
	uprt(r - l + 1, rt);
}

int main()
{
	int n, k;
	char str[2];
	int a;
	while (~scanf("%d%d", &n, &k))
	{
		stack<int>pre;
		build(1, n, 1);
		for (int i = 0; i < k; i++)
		{
			scanf("%s", str);
			if (str[0] == 'D')
			{
				scanf("%d", &a);
				pre.push(a);
				updata(a, 0, 1, n, 1);
				continue;
			}
			if (str[0] == 'Q')
			{
				scanf("%d", &a);
				lsum[1] = 0;
				rsum[1] = 0;
				query(a, 1, n, 1);
				printf("%d\n", ans);
				continue;
			}
			if (!pre.empty())
			{
				while (pre.empty() && bb[pre.top()])
					pre.pop();
				updata(pre.top(), 1, 1, n, 1);
				pre.pop();
			}
		}
	}
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值