HDU 1540 Tunnel Warfare (線段樹,區間合併)


Tunnel Warfare

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 9270    Accepted Submission(s): 3609


Problem Description
During the War of Resistance Against Japan, tunnel warfare was carried out extensively in the vast areas of north China Plain. Generally speaking, villages connected by tunnels lay in a line. Except the two at the ends, every village was directly connected with two neighboring ones.

Frequently the invaders launched attack on some of the villages and destroyed the parts of tunnels in them. The Eighth Route Army commanders requested the latest connection state of the tunnels and villages. If some villages are severely isolated, restoration of connection must be done immediately!
 

Input
The first line of the input contains two positive integers n and m (n, m ≤ 50,000) indicating the number of villages and events. Each of the next m lines describes an event.

There are three different events described in different format shown below:

D x: The x-th village was destroyed.

Q x: The Army commands requested the number of villages that x-th village was directly or indirectly connected with including itself.

R: The village destroyed last was rebuilt.
 

Output
Output the answer to each of the Army commanders’ request in order on a separate line.
 

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

題意:D表示摧毀某個村莊,R表示修復最後一個被摧毀的村莊,Q表示詢問包含x在內的最大連續區間

思路:利用線段樹求最大連續區間,我最近剛學線段樹,這種題目對我來說還是有難度的,參考了別人的博客,才理解

上代碼

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<string>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define maxn 50010
int len, R, L;
struct node
{
	int llen, rlen;//左右最大連續區間
}tree[maxn << 2];
void build(int l, int r, int rt)
{
	tree[rt].llen = tree[rt].rlen = r - l + 1;//一開始左右最大連續區間都是滿的
	if (l == r)
		return;
	int m = (l + r) / 2;
	build(ls);
	build(rs);
}
void updata(int l, int r, int rt, int id, int a)
{
	if (l == r)
	{
		tree[rt].llen = tree[rt].rlen = a;//摧毀或者回復
		return;
	}
	int m = (l + r) / 2;
	if (id <= m)
		updata(ls, id, a);
	else
		updata(rs, id, a);
	//以下是代碼的核心
	//父區間的最大左右區間
	tree[rt].llen = tree[rt << 1].llen;
	tree[rt].rlen = tree[rt << 1 | 1].rlen;
	if (tree[rt].llen == m - l + 1)//如果左子樹區間滿了,父左區間要加上右子樹的左區間
		tree[rt].llen += tree[rt << 1 | 1].llen;
	if (tree[rt].rlen == r - m)//如果右子樹右區間滿了,父右區間要加上左子樹的右區間
		tree[rt].rlen += tree[rt << 1].rlen;
}
//查詢的話,就從那個數開始,看能不能和自己左(右)區間的合併
void query(int l, int r, int rt, int id)
{
	if (l == r)
	{
		if (tree[rt].llen)
			R = L = 1;
		else
			R = L = 0;
		len = tree[rt].llen;
		return;
	}
	int m = (l + r) / 2;
	//左區間滿向右,右區間滿向左
	if (id <= m)
	{
		query(ls, id);
		if (L)
			len += tree[rt << 1 | 1].llen;//能和右區間的合併
		if (tree[rt << 1 | 1].llen<r - m)//父區間不滿,不能再和右區間合併了
			L = 0;
	}
	else
	{
		query(rs, id);
		if (R)
			len += tree[rt << 1].rlen;//能和左區間合併
		if (tree[rt << 1].rlen<m - l + 1)//父區間不滿,不能再和左區間合併了
			R = 0;
	}
}
int main()
{
	//freopen("Text.txt", "r", stdin);
	int n, m, s[maxn], sn, id;
	char ch[2];
	while (scanf("%d%d", &n, &m) != EOF)
	{
		build(1, n, 1);
		sn = 0;
		while (m--)
		{
			scanf("%s", ch);
			if (ch[0] == 'D')
			{
				scanf("%d", &id);
				s[++sn] = id;
				updata(1, n, 1, id, 0);
			}
			else if (ch[0] == 'R')
			{
				if (sn)
					updata(1, n, 1, s[sn], 1), sn--;//修復最後一個被破壞的村莊
			}
			else
			{
				scanf("%d", &id);
				query(1, n, 1, id);
				printf("%d\n", len);
			}
		}
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章