Tunnel Warfare(hdu 1540)

来源:互联网 发布:HIS系统数据库类型 编辑:程序博客网 时间:2024/06/05 01:20

Tunnel Warfare

TimeLimit: 4000/2000 MS (Java/Others)    Memory Limit:65536/32768 K (Java/Others)
Total Submission(s): 10244    Accepted Submission(s): 4023

ProblemDescription

During the War of Resistance AgainstJapan, tunnel warfare was carried out extensively in the vast areas of northChina Plain. Generally speaking, villages connected by tunnels lay in a line.Except the two at the ends, every village was directly connected with twoneighboring ones.

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

 

 

Input

The first line of the input contains twopositive integers n and m (n, m ≤ 50,000) indicating the number of villages andevents. 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 wasdirectly or indirectly connected with including itself.

R: The village destroyed last was rebuilt.

 

 

Output

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

 

 

SampleInput

7 9

D 3

D 6

D 5

Q 4

Q 5

R

Q 4

R

Q 4

 

 

SampleOutput

1

0

2

4

 

题意

        N个村庄连成一条直线,相邻的两个村庄有一条道路相连,现在有M次操作,分以下3种。(1)D x 表示摧毁第x个村庄以及和它相连的道路。(2)Q x 表示查询第x个村庄和多少个村庄相连接(包括第x个村庄自身),如果第x个村庄被毁则为0。(3)R 重建最后被毁的村庄和相应道路。对于每次的查询操作输出相应结果。

 

思路一

        利用线段树最基本的用法求最大值和最小值来求解,想法比较巧妙。对于村庄的区间[1,N],对其建立线段树,这个线段树中要维护两个值,分别是当前区间中被毁村庄编号的最小值和最大值minv,maxv,但是要注意对于单点x来说,如果该存庄没有被毁则minv=0,maxv=N+1便于计算,否则minv=maxv=x,大体思路就是找到当前村庄左边被毁村庄的最大值和右边被毁村庄的最小值。举个简单的例子:

minv

8

8

3

8

8

6

8

maxv 

0

0

3

0

0

6

0

村庄

1          

2          

3(被毁)

4          

5          

6(被毁 )

7         


       查询被毁的村庄:如果要查询第3个村庄,[1,3]中被毁村庄maxv的最大值l=3和[3,7]中被毁村庄minv的最小值r=3,即l==r时输出0即可       查询没有被毁的村庄:如果要查询第4个村庄,那么只需要找到[1,4]中被毁村庄maxv的最大值l=3和[4,7]中被毁村庄minv的最小值r=6,那么l-r-1=2便是答案;如果要查询第2个村庄,同理[1,2]中被毁村庄maxv的最大值l=0,[2,7]中被毁村庄minv的最小值r=3,r-l-1=2便是答案,这里也解释了为什么要把没有被毁的村庄的minv置为N+1,maxv置为0。

       综上可以根据线段树的两次查询得到一组答案

if (l == r) ans = 0;

else ans = r– l – 1;

 

代码

#include<stack>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int maxn = 50500;#define node tree[id]#define lson tree[id*2]#define rson tree[id*2+1]int n, m;struct Tree {int left, right, minv, maxv;}tree[maxn * 4];void pushup(int id) {node.maxv = max(lson.maxv, rson.maxv);node.minv = min(lson.minv, rson.minv);}void build(int id, int le, int ri) {node.left = le;node.right = ri;node.maxv = 0;node.minv = n + 1;if (le == ri) return;int mid = (le + ri) >> 1;build(id * 2, le, mid);build(id * 2 + 1, mid + 1, ri);}int query_min(int id, int x, int y) {//查询区间内minv的最小值if (node.left >= x && node.right <= y) {return node.minv;}pushup(id);int ans = n + 1;int mid = (node.left + node.right) >> 1;if (x <= mid) ans = min(ans, query_min(id * 2, x, y));if (y > mid) ans = min(ans, query_min(id * 2 + 1, x, y));return ans;}int query_max(int id, int x, int y) {//查询区间内maxv的最大值if (node.left >= x && node.right <= y) {return node.maxv;}pushup(id);int ans = 0;int mid = (node.left + node.right) >> 1;if (x <= mid) ans = max(ans, query_max(id * 2, x, y));if (y > mid) ans = max(ans, query_max(id * 2 + 1, x, y));return ans;}void update(int id, int pos, int v) {//更新区间,v==-1代表重建if (node.left == node.right) {if (-1 != v) node.maxv = node.minv = v;else {node.maxv = 0;node.minv = n + 1;}return;}int mid = (node.left + node.right) >> 1;if (pos <= mid) update(id * 2, pos, v);else update(id * 2 + 1, pos, v);pushup(id);}int main() {char op;int x;while (scanf("%d%d", &n, &m) == 2) {stack<int> st;//记录被毁顺序build(1, 1, n);while (m--) {scanf(" %c", &op);if ('D' == op) {scanf("%d", &x);update(1, x, x);st.push(x);}else if ('Q' == op) {scanf("%d", &x);int ans;int l = query_max(1, 1, x);int r = query_min(1, x, n);if (l == r) ans = 0;else ans = r - l - 1;printf("%d\n", ans);}else {int tmp = st.top();st.pop();update(1, tmp, -1);}}}return 0;}


思路二

        把村庄抽象成点,1表示未摧毁,0表示已摧毁。线段树中要维护3个值,ls,rs,ms。ls表示从该结点对应的区间最左端开始往右的连续区间长度,同理rs表示从对应区间最右端开始往左的连续区间长度,ms表示当前结点对应区间的最长连续区间长度(全是1)。

        通过递推即可求解,具体递推关系看代码,这就是线段树的区间合并问题。

update(int id, int pos, int v)用于破坏或修复第pos个村庄。

query(int id, int pos)用于查询以id为根结点对应区间中,包含v的最大连续区间长度。

 

代码

#include<stack>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int maxn = 50500;#define node tree[id]#define lson tree[id*2]#define rson tree[id*2+1]int n, m;struct Tree {int left, right, ls, rs, ms;}tree[maxn << 2];void build(int id, int le, int ri) {node.left = le;node.right = ri;node.ls = node.rs = node.ms = ri - le + 1;if (le == ri) return;int mid = (le + ri) >> 1;build(id * 2, le, mid);build(id * 2 + 1, mid + 1, ri);}void update(int id, int pos, int v) {//破坏或修复第pos个村庄if (node.left == node.right) {if (0 == v) node.ls = node.rs = node.ms = 0; //v==0破坏else node.ls = node.rs = node.ms = 1; //v==1修复return;}int mid = (node.left + node.right) >> 1;if (pos <= mid) update(id * 2, pos, v);else update(id * 2 + 1, pos, v);node.ls = lson.ls;//当前结点的左区间node.rs = rson.rs;//当前结点的右区间node.ms = max(lson.rs + rson.ls, max(lson.ms, rson.ms));//当前结点对应的最大连续区间一定是//左结点的最大连续区间,右结点最大连续区间,左结点右区间+右结点左区间 这三者的最大值if (lson.ls == lson.right - lson.left + 1) node.ls += rson.ls;//左结点的左区间满,那么当前结点的左区间还要再加上右结点的左区间if (rson.rs == rson.right - rson.left + 1) node.rs += lson.rs;//当前结点的右区间同理}int query(int id, int pos) {if (node.left == node.right || 0 == node.ms || node.right - node.left + 1 == node.ms) {//递归到单点或者 对应的区间为满或空就可以直接返回了return node.ms;}int ans;int mid = (node.left + node.right) >> 1;if (pos <= mid) {if (pos >= lson.right - lson.rs + 1) ans = query(id * 2, pos) + query(id * 2 + 1, mid + 1);//如果pos刚好处在左结点最右端的连续区间上面那么就还要再加上右结点对应最左端的连续区间else ans = query(id * 2, pos);//否则只要看左结点就可以了}else {//右结点同理if (pos <= rson.left + rson.ls - 1) ans = query(id * 2 + 1, pos) + query(id * 2, mid);else ans = query(id * 2 + 1, pos);}return ans;}int main() {char op;int x;while (scanf("%d%d", &n, &m) == 2) {stack<int> st;build(1, 1, n);while (m--) {scanf(" %c", &op);if ('D' == op) {scanf("%d", &x);update(1, x, 0);st.push(x);}else if ('Q' == op) {scanf("%d", &x);int ans = query(1, x);printf("%d\n", ans);}else {int tmp = st.top();st.pop();update(1, tmp, 1);}}}return 0;}

原创粉丝点击