hdu1540-Tunnel Warfare 线段树区间合并

来源:互联网 发布:windows old有什么用 编辑:程序博客网 时间:2024/05/21 22:52

Tunnel Warfare

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


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 9D 3D 6D 5Q 4Q 5RQ 4RQ 4
 

Sample Output
1024
 
题目大意:给你n个村庄,相距为1的村庄是相互连接的,然后给出一系列操作,D表示让村庄消失,Q表示询问当前状态下最长的连接的村庄的数量,R表示恢复最后消失的村庄。

解题思路:这道题是求最长连续区间,可以使用线段树来解决,存在的村庄置1,消失的置0,然后我们需要使用一个栈来存贮消失的村庄,用于村庄恢复,在这里我们需要使用到的是区间合并知识,所谓区间合并就是当一个区间的左区间是满的时候则这个区间的左区间长度为左区间长度加上右区间的左区间长度,当一个区间的右区间是满的时候,这个区间的右区间长度为右区间长度加上左区间的右区间长度,具体可以参看代码描述,这里都是使用递归的思想来解决问题。

ac代码:
#include <cstdio>#include <iostream>using namespace std;#define Max 50005int s[Max], top;//想当于栈的作用 struct node{int left, right, maxn;//记录连续左区间长度、连续右区间长度、连续的最大长度 }sum[Max<<2];void Build(int l, int r, int rt){sum[rt].left = sum[rt].right = sum[rt].maxn = r - l +1; //初始都是1所以赋值为区间长度 if(l == r)return ;int m = (l + r) >> 1;//递归建树 Build(l, m, rt<<1);Build(m+1, r, rt<<1|1);}void update(int l, int r, int t, int x, int rt)//更新节点 {if(l == r){//搜索到叶子节点 位置 if(x == 1) sum[rt].left = sum[rt].right = sum[rt].maxn = 1;//恢复村庄 else sum[rt].left = sum[rt].right = sum[rt].maxn = 0;//让村庄消失 return ;}int m = (l + r) >> 1;if(t <= m) update(l, m, t, x, rt<<1);//回溯向上更新 else update(m+1, r, t, x, rt<<1|1);sum[rt].left = sum[rt<<1].left;sum[rt].right = sum[rt<<1|1].right;sum[rt].maxn = max(max(sum[rt<<1].maxn, sum[rt<<1|1].maxn), sum[rt<<1].right + sum[rt<<1|1].left);//很重要 if(sum[rt<<1].left == m - l + 1) sum[rt].left += sum[rt<<1|1].left;//左区间满,合并 if(sum[rt<<1|1].right == r - m ) sum[rt].right += sum[rt<<1].right;//右区间满,合并 }int query(int l, int r, int t, int rt)//查询最大连续区间长度 {if(l == r || sum[rt].maxn == 0 || sum[rt].maxn == r - l + 1){//当搜索到叶子节点或者空节点或者节点已满 return sum[rt].maxn;}int m = (l + r) >> 1;if(t <= m){if(t >= m - sum[rt<<1].right +1)//因为t<=mid,看左子树,a[2*i].r-a[2*i].rs+1代表左子树右边连续区间的左边界值,如果t在左子树的右区间内,则要看右子树的左区间有多长并返回    return query(m+1, r, m+1, rt<<1|1) + query(l, m, t, rt<<1);else return query(l, m, t, rt<<1);//如果不在左子树的右边界区间内,则只需要看左子树  }else{if(t <= m+1 + sum[rt<<1|1].left -1)//同理    return query(m+1, r, t, rt<<1|1) + query(l, m, m, rt<<1);else return query(m+1, r, t, rt<<1|1);}}int main(){int n, m, x;char str[3];while(~scanf("%d%d", &n, &m)){top = 0;Build(1, n, 1);while(m--){scanf("%s", str);if(str[0] == 'D'){scanf("%d", &x);s[top++] = x;//入栈 update(1, n, x, 0, 1);}else if(str[0] == 'Q'){scanf("%d", &x);printf("%d\n", query(1, n, x, 1));}else if(str[0] == 'R'){if(x > 0){ x = s[--top];//出栈 update(1, n, x, 1, 1);}}}}return 0;}

题目链接地址:点击打开链接http://acm.hdu.edu.cn/showproblem.php?pid=1540
原创粉丝点击