Tunnel Warfare (HDU - 1540)線段樹

在抗日戰爭期間,華北平原廣大地區進行了大規模的隧道戰。 一般來說,通過隧道連接的村莊排成一列。 除了兩端,每個村莊都與兩個相鄰的村莊直接相連。
入侵者經常對一些村莊發動襲擊並摧毀其中的部分隧道。 八路軍指揮官要求最新的隧道和村莊連接狀態。 如果某些村莊嚴重隔離,必須立即恢復連接!

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\n",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\n",*r-*l-1);
				}
			}
			else if(st.size())
			{
				stt.erase(st.top());
				st.pop();
			}
		}
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章