HDU1540 Tunnel Warfare (线段树区间合并)

来源:互联网 发布:最新好听网络歌曲 编辑:程序博客网 时间:2024/05/22 05:17

Tunnel Warfare

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


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
 

Source
POJ Monthly


题意:有n个村庄,每个村庄都用地道连接着,分为三种操作,R表示修复最后一个被破坏的村庄,D x表示破话村庄x, Q x表示求包括村庄x的最长连续区间。

思路:线段树的区间合并题目,对于求一个连续最长区间,分三种情况,①是左子树最长连续区间,②是右子树最长连续区间,③是左子树最长右连续区间+右子树最长左连续区间的值,这三种情况取最大值即可。具体可看代码注释,很详细。


AC代码如下:

#include <cstdio>#include <stack>#include <cstring>#include <algorithm>#define LL long longusing namespace std;const int maxn = 5e4 + 10;int n,m,s[maxn],top;char op[3];struct node{int l,r;    //以rt为根结点的左右区间l表示左端点,r表示右端点 int ls,rs,ms;//ls:左起最大连续区间,rs:右起最大连续区间 ms:最大连续区间 }p[maxn << 2];void build(int l, int r, int rt){//建树 & 初始化 p[rt].l = l; p[rt].r = r;  //以rt为根结点的左右区间[l,r] p[rt].ls = p[rt].ms = p[rt].rs = r - l + 1;   //初始该区间的所有值均为1,故为区间大小 if(l != r){int m = (l + r) >> 1;build(l,m,rt << 1);build(m+1,r,rt << 1 | 1);}}void update(int rt, int x, int flag){//更新x节点的状态 if(p[rt].l == p[rt].r){if(flag) p[rt].ls = p[rt].ms = p[rt].rs = 1;  //修复 else p[rt].ls = p[rt].ms = p[rt].rs = 0;    //破坏 return ;}int m = (p[rt].l + p[rt].r) >> 1;if(x <= m) update(rt << 1,x,flag);  //要破坏或者修复的点在左子树 else update(rt << 1 | 1,x,flag);    //要破坏或者修复的点在右子树 p[rt].ls = p[rt << 1].ls;     //父节点的左区间最大连续长度等与左子树的, p[rt].rs = p[rt << 1 | 1].rs; //着重分析最后两个if语句 p[rt].ms = max(max(p[rt << 1].ms,p[rt << 1 | 1].ms),p[rt << 1].rs + p[rt << 1 | 1].ls);//区间的最大连续区间分三种情况:左子树最大连续区间,右子树最大连续区间,左子树右连续+右子树左连续 if(p[rt << 1].ls == p[rt << 1].r - p[rt << 1].l + 1) p[rt].ls += p[rt << 1 | 1].ls;//左子树的左连续区间长度等于 左子树区间大小,则其父节点的长度应为加上右子树左连续长度 if(p[rt << 1 | 1].rs == p[rt << 1 | 1].r - p[rt << 1 | 1].l + 1) p[rt].rs += p[rt << 1].rs;//右子树右连续长度等于右子树区间大小,则其父节点的长度为再加上左子树的右连续的长度 }int query(int rt, int x){//查询含有节点x的最长连续区间//找到x节点或者最长连续区间为0或者区间值全为1 if(p[rt].l == p[rt].r || p[rt].ms == 0 || p[rt].ms == p[rt].r - p[rt].l + 1)return p[rt].ms;int m = (p[rt].l + p[rt].r) >> 1;if(x <= m){ //当前结点在左子树 //当前节点大于等于左子树右连续区间的左端点值 ,则要再计算上右子树的左连续区间if(x >= p[rt << 1].r - p[rt << 1].rs + 1)return query(rt << 1,x) + query(rt << 1 | 1,m+1);elsereturn query(rt << 1,x);}else{//当前节点小于等于右子树左连续区间的右端点值,则要计算上左子树的右连续区间 if(x <= p[rt << 1 | 1].l + p[rt << 1 | 1].ls - 1)return query(rt << 1 | 1,x) + query(rt << 1, m);elsereturn query(rt << 1 | 1,x);  }}int main(){while(~scanf("%d%d",&n,&m)){build(1,n,1);int num;top = 0;while(m --){scanf("%s",op);if(op[0] == 'R'){if(num > 0){num = s[-- top];update(1,num,1);}}else{scanf("%d",&num);if(op[0] == 'D'){s[top ++] = num;update(1,num,0);}else{printf("%d\n",query(1,num));}}}}return 0;}