hdu1540(线段树+二分)

问题:在抗日战争期间,华北平原广大地区进行了大规模的隧道战。 一般来说,通过隧道连接的村庄排成一列。 除了两端,每个村庄都与两个相邻的村庄直接相连。
入侵者经常对一些村庄发动袭击并摧毁其中的部分隧道。 八路军指挥官要求最新的隧道和村庄连接状态。 如果某些村庄严重隔离,必须立即恢复连接!
输入:输入的第一行包含两个正整数n和m(n,m≤50,000),表示村庄和事件的数量。 接下来的m行中的每一行描述一个事件。以下所示的不同格式描述了三种不同的事件:
D x:第x个村庄被毁。
Q x:指挥官询问第x个村庄与其直接或间接相关的村庄数量。
R:最后毁坏的村庄被重建了。
输出:按顺序输出每个指挥官询问的答案。
分析:由于刚刚接触这一题的时候,我并不知道线段树维护连续区间这个东西,我昨完后看题解发现很多都是这个写法,但是这里我就提供一下我自己的一个思路。
我也是建线段树,但是我的线段树只维护了一个东西,就是这个区间是不是都被摧毁,或者都安全,比如村庄1存活,村庄2被摧毁,那么线段树区间对应的【1,1】,就应该是1(表示这段区间的房子存活),【2,2】就应该是0(表示这段区间的房子被摧毁),【1,2】应该是0,(表示这段区间没有全都存活)。
那么对于问题我们如果解决?我是这么分析的,由于是判断这个村庄左右存活的连续村庄与该村庄是否相连,那么就对该询问村庄的左右两边进行各进行一次二分查找答案。
如果询问村庄4,那么就去找前面【1,3】区间和后方【5,7】区间分别有多少个连续的存活村庄。
这要分两种情况,对于村庄3前方的区间,我们要这么分析,把前方区间二分成左右两个区间,一旦右区间不是全部都存活,那么没有必要去判断左区间有多少个连续存活的村庄(因为必须和询问的村庄连续都存活,一旦右区间有断层,那么左区间不管有多少个连续都无法对答案造成影响),如果左区间全部存活,则判断右区间,判断右区间最多有多少个连续,再加上整个左区间,即询问村庄左半部分区间的连续村庄数;如果左区间没有全部存活,这把左区间细分区间,继续判断。
这样就解决了询问村庄,左区间的问题,右区间是同理,只是要把优先判断的区间变成优先判断左区间是不是连续存活而已。
在这里插入图片描述这样每个询问去两遍二分询问村庄左右区间既可以出答案。对于修复村庄,我们只要把所有破坏顺序记录下来,一旦修复,择去修改线段树上对应区间从0变成1即可。
这是提供另一种做法吧,如果有任何疑问欢迎询问。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<stack>
#include<cstring>
using namespace std;
const long long max_ = 50000 + 50;
int tree_[max_ * 4], cuihui[max_], cnum = 0, pan[max_];
int n, m;
void update(int L, int R, int aim, int node,int v) {
	if (L == R) {
		tree_[node] = v;
	}
	else {
		int mid = (L + R) / 2, Ltree = node * 2, Rtree = node * 2 + 1;
		if (mid >= aim)update(L, mid, aim, Ltree,v);
		else update(mid + 1, R, aim, Rtree,v);
		tree_[node] = tree_[Ltree] && tree_[Rtree];
	}
}

int ask(int L, int R, int aimL, int aimR, int node) {
	if (aimL > aimR) return 0;
	if (L >= aimL && R <= aimR) {
		return tree_[node];
	 }
	else {
		int mid = (L + R) / 2, Ltree = node * 2, Rtree = node * 2 + 1;		
			if (mid >= aimR) return  ask(L, mid, aimL, aimR, Ltree);
			else
			{ 
				if(mid < aimL)
				 return ask(mid + 1, R, aimL, aimR, Rtree);
			else {
				return ask(L, mid, aimL, aimR, Ltree) && ask(mid + 1, R, aimL, aimR, Rtree);
			}
			}
		
	}
}
int erfen(int L, int R) {
	int mid = (L + R) / 2;
	if (L > R) return 0;
	//ask(int L, int R, int aimL, int aimR, int node)
	if (ask(1, n, mid, R, 1)) {
		//右端点可以,看左端点;
		return (R - mid + 1) + erfen(L, mid-1);
	}
	else {//右端点不行,细分右端点继续判断
		return erfen(mid+1,R);
	}
}

int erfen2(int L, int R) {
	int mid = (L + R) / 2;
	if (L > R) return 0;
	//ask(int L, int R, int aimL, int aimR, int node)
	if (ask(1, n, L, mid, 1)) {
		//左端点可以,看右端点;
		return (mid -L + 1) + erfen2(mid+1, R);
	}
	else {//左端点不行,细分左端点继续判断
		return  erfen2(L, mid-1);
	}
}
int main() {
	
	while (~scanf("%d%d", &n, &m)) {
		for (int i = 0; i <= n * 4; i++)tree_[i] = 1;
		for (int i = 1; i <= m; i++) {
			char ch;
			getchar(); scanf("%c", &ch);
			if (ch == 'D') {
				int a;
				scanf("%d", &a);
				cuihui[++cnum] = a;
				pan[a] = 1;
				update(1, n, a, 1, 0);
			}
			else {
				if (ch == 'Q') {
					int a;
					scanf("%d", &a);
					if (pan[a] == 1) {
						cout << "0" << endl;
					}
					else {
						int t1 = erfen(1, a - 1);
						int t2 = erfen2(a + 1, n);
						cout << t1 + t2 + 1 << endl;
					}
				}
				else {
					int t = cuihui[cnum--];
					pan[t] = 0;
					update(1, n, t, 1, 1);
				}
			}
		}
		memset(tree_, 0,sizeof(tree_)); memset(pan, 0, sizeof(pan)); memset(cuihui, 0, sizeof(cuihui));
		cnum = 0;
	}
	
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值