Tunnel Warfare (HDU

在抗日战争期间,华北平原广大地区进行了大规模的隧道战。 一般来说,通过隧道连接的村庄排成一列。 除了两端,每个村庄都与两个相邻的村庄直接相连。
入侵者经常对一些村庄发动袭击并摧毁其中的部分隧道。 八路军指挥官要求最新的隧道和村庄连接状态。 如果某些村庄严重隔离,必须立即恢复连接!

Input
输入的第一行包含两个正整数n和m(n,m≤50,000),表示村庄和事件的数量。 接下来的m行中的每一行描述一个事件。

以下所示的不同格式描述了三种不同的事件:
D x:第x个村庄被毁。
Q x:指挥官询问第x个村庄与其直接或间接相关的村庄数量。
R:最后毁坏的村庄被重建了。

Output
按顺序输出每个指挥官询问的答案。

Sample Input
7 9
D 3
D 6
D 5
Q 4
Q 5
R
Q 4
R
Q 4
Sample Output
1
0
2
4

思路:
如果村庄x被摧毁,那与他相连的村庄就是0个,如果没有被摧毁那么就等于,x左边第一个被摧毁村庄到x右边第一个被摧毁村庄之间的村庄的数量,很容易就可以想到用线段树可以做,直接上代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=50010;
int n,m,a[N];//a[x]==1,表示x没有被摧毁
struct node
{
	int l,r,sum;//sum区间[l,r]之间没被摧毁的村庄的数量
}tr[N<<2];
void pushup(int rt)
{
	tr[rt].sum=tr[rt<<1].sum+tr[rt<<1|1].sum;
}
void build(int rt,int l,int r)
{
	if(l==r) tr[rt]={l,r,1},a[l]=1;
	else 
	{
		tr[rt]={l,r};
		int mid = l+r>>1;
		build(rt<<1,l,mid);
		build(rt<<1|1,mid+1,r);
		pushup(rt);
	}
}
void updata(int rt,int x,int d)
{
	if(tr[rt].l==x&&tr[rt].r==x) a[x]=tr[rt].sum=d;
	else
	{
		int mid =tr[rt].l+tr[rt].r>>1;
		if(x<=mid) updata(rt<<1,x,d);
		else updata(rt<<1|1,x,d);
		pushup(rt);
	}
}
int query(int rt,int l,int r)//查询[l,r]之间被被摧毁村庄的数量
{
	if(tr[rt].l>=l&&tr[rt].r<=r) return tr[rt].sum;
	int mid =tr[rt].l+tr[rt].r>>1,res=0;
	if(l<=mid) res+=query(rt<<1,l,r);
	if(r>mid) res+=query(rt<<1|1,l,r);
	return res;
}
int query1(int rt,int x)//查询x左边第一个被摧毁的村庄的坐标
{
	if(tr[rt].l==tr[rt].r) return (tr[rt].sum==1?0:tr[rt].l);//这个节点没被摧毁就返回0,最小的点,最终的答案可能在这个节点左或者右,返回0,一定不影响答案。
	int mid= tr[rt].l+tr[rt].r>>1;
	if(x>mid) 
	{
		if(query(1,mid+1,x)==x-mid) return query1(rt<<1,mid);//如果mid到x都没被摧毁,第一个被摧毁的村庄一定在mid左边
		else return max(query1(rt<<1,mid),query1(rt<<1|1,x));//否则取最大值,就是离x最近的
	}
	else 
	{
		return query1(rt<<1,x);//x<=mid,一定在[tr[rt].l,x]
	}
}
int query2(int rt,int x)//查询x右边第一个被摧毁的村庄
{
	if(tr[rt].l==tr[rt].r) return (tr[rt].sum==1?n+1:tr[rt].l);//与上面一样,n+1不影响结果
	int mid= tr[rt].l+tr[rt].r>>1;
	if(x<=mid) 
	{
		if(query(1,x,mid)==mid-x+1) return query2(rt<<1|1,mid+1);//一定在mid+1右边
		else return min(query2(rt<<1,x),query2(rt<<1|1,mid+1));//在x右边就得取最小值
	}
	else 
	{
		return query2(rt<<1|1,x);//在x以右
	}
}
int main()
{
	while(~scanf("%d%d",&n,&m))
	{
		build(1,1,n);
		stack<int> st;
		while(m--)
		{
			char op[2];
			scanf("%s",op);
			int x;
			if(*op=='D') 
			{
				scanf("%d",&x); 
				st.push(x);//用栈存储被摧毁的村庄
				updata(1,x,0);
			}
			else if(*op=='Q')
			{
				scanf("%d",&x);
				if(a[x]) printf("%d
",query2(1,x)-1-query1(1,x));
				else puts("0");
			}
			else if(st.size())
			{
				updata(1,st.top(),1);
				st.pop();
			}
		}
	}
}

看了个大佬的代码,思路是一样的,用红黑树去求只用了265ms,上面哪个是1800+ms,

#include<bits/stdc++.h>
using namespace std; 
int n,m;
int main()
{
	while(~scanf("%d%d",&n,&m))
	{
		char op[2];
		int x;
		stack<int> st; set<int> stt;//都保存被摧毁的村庄
		stt.insert(0);//预处理边界,避免麻烦
		stt.insert(n+1);
		while(m--)
		{
			scanf("%s",op);
			if(*op=='D')
			{
				scanf("%d",&x);
				st.push(x);
				stt.insert(x);//将x插入
			}
			else if(*op=='Q')
			{
				scanf("%d",&x);
				if(stt.find(x)!=stt.end()) puts("0");//x没被摧毁
				else
				{
					auto l=stt.upper_bound(x);//返回最小的大于x的被摧毁村庄,就是x右边第一个被摧毁村庄,lower_bound()是一样的因为stt里面没有x
					auto r=l--;//再减1就是最大的小于x的被摧毁村庄
					printf("%d
",*r-*l-1);
				}
			}
			else if(st.size())
			{
				stt.erase(st.top());
				st.pop();
			}
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/neflibata/p/12871750.html