HDU 1540 Tunnel Warfare线段树解法及分块解法

来源:互联网 发布:淘宝宝贝创建时间 编辑:程序博客网 时间:2024/06/06 19:25

Tunnel Warfare

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


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
        题意:一些村庄在一条直线上,每个村庄都与相邻的两个村庄相连。现在有三种操作:1.破坏某个村庄。2.修复最近破坏的一个村庄。3.询问某个村庄与它直接或间接相连的村庄的数量。

       思路:这个题解法有两种,线段树或者暴力分块。目的都是找出询问村庄的左边界和右边界,然后直接计算出结果。

分块代码:

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#define MAX 50005using namespace std;struct Node{int left,right;}node[MAX];int Stack[MAX],top;int n,m;int area,prot;void Distory(int pos) //在破坏时,对区域进行更新(down和up之间)。{int down = pos/area*area;int up = min(n,(pos/area + 1)*area - 1);node[pos].left = node[pos].right = pos;for(int j = pos - 1; j >= down && node[j].left != node[j].right; j--)node[j].right = pos;for(int j = pos + 1; j <= up && node[j].left != node[j].right; j++)node[j].left = pos;return ;}void Rebuild(int pos) //在修复是,对区域进行更新 {int down = pos/area*area;int up = min(n,(pos/area + 1)*area - 1);int left = pos == down ? down - 1 : node[pos - 1].left,right= pos == up ? up + 1 : node[pos + 1].right;node[pos].left = left,node[pos].right = right;for(int j = pos - 1; j >= down && node[j].left != node[j].right; j--)node[j].right = right;for(int j = pos + 1; j <= up && node[j].left != node[j].right; j++)node[j].left = left;return ;}int find_left(int pos) //找到左边界 {return (node[pos].left == pos || node[pos].left == -1) ? node[pos].left : find_left(node[pos].left);}int find_right(int pos) //找到右边界 {return (node[pos].right == pos || node[pos].right == n) ?  node[pos].right : find_right(node[pos].right);}void PUT(){for(int j = 0; j < n; j++)cout << node[j].left << "|" << node[j].right << endl;}int main( ){while(scanf("%d%d",&n,&m) != EOF){top = 0;area = sqrt(n);if(n%area > 0) area++;for(int j = 0; j < area; j++) //先做分块预处理,之后的所有处理都在对应块中进行 for(int k = 0; k < area; k++)node[area*j + k].left = area*j - 1,node[area*j + k].right = min(area*(j + 1),n);while(m--){getchar();char c = getchar();int poit;if(c == 'D'){scanf("%d",&poit);poit--;Distory(poit);Stack[top++] = poit;}if(c == 'Q'){scanf("%d",&poit);poit--;int ans = find_right(poit) - find_left(poit) - 1;printf("%d\n",ans == -1 ? 0 : ans);}if(c == 'R'){poit = Stack[--top];Rebuild(poit);}}}return 0;}
         分块就是O(n)暴力的优化,优化后的复杂度是O(sqrt(n))。在此处的复杂度一共是O(m*sqrt(n)).。


线段树代码:

#include<iostream>#include<cstring>#include<cstdio>#define MAX 50004using namespace std;struct Node{int left,mid,right;bool OK;}Tree[MAX*3];int Stack[MAX],top;int node_tree[MAX];int l,r,n,m;void BuildTree(int left,int right,int pos) //建树 {Tree[pos].left = left,Tree[pos].right = right;Tree[pos].mid = (left + right) >> 1;Tree[pos].OK = true;if(left == right){node_tree[left] = pos;return ;}BuildTree(left,(right + left)>>1,pos<<1);BuildTree(((right + left)>>1) + 1,right,(pos<<1) + 1);return ;}void DistoryNode(int pos) //在破坏时更新 {Tree[pos].OK = false;if(pos == 1) return ;DistoryNode(pos>>1);}void RebuildNode(int pos) //在修复是更新 {Tree[pos].OK = true;if(pos == 1 || !Tree[pos^1].OK) return ;RebuildNode(pos>>1);}void LeftQueryTree(int node,int pos); //寻找区间内最左边的被破坏的村庄 void RightQueryTree(int node,int pos);//寻找区间内最右边的被破坏的村庄 int main( ){while(cin >> n >> m){BuildTree(1,n,1);while(m--){getchar(); char c = getchar();int node;if(c == 'D'){scanf("%d",&node);Stack[top++] = node;DistoryNode(node_tree[node]);}if(c == 'Q'){scanf("%d",&node);l = 0;RightQueryTree(node,1);//左边界在[1,node]区间中最右边的点 r = n + 1;LeftQueryTree(node,1); //右边界在[node,n - 1]区间中最左边的点 int ans = 0;if(l == r) ans = 0;else ans = r - l - 1;printf("%d\n",ans);}if(c == 'R'){node = Stack[--top];RebuildNode(node_tree[node]);}}}return 0;}void RightQueryTree(int node,int pos){if(Tree[pos].OK || l != 0){return ;}if(Tree[pos].left == Tree[pos].right){if(!Tree[pos].OK) l = Tree[pos].left;return;}if(Tree[pos].mid < node){RightQueryTree(node,pos<<1|1); //优先搜索右子树 }RightQueryTree(node,pos<<1); return ;}void LeftQueryTree(int node,int pos){if(Tree[pos].OK || r != n + 1){return ;}if(Tree[pos].left == Tree[pos].right){if(!Tree[pos].OK) r = Tree[pos].left;return ;}if(Tree[pos].mid >= node){LeftQueryTree(node,pos<<1);  //优先搜索左子树 }LeftQueryTree(node,pos<<1|1);return ; } 
          线段树做法每步操作的复杂度O(log(n)),总复杂度是O(m*log(n))。


          这个是线段树模板题,所以往上的很多题解也都是线段树。而这个题的数据量完全可以用分块解决,所以,我就自己把这个题再拉出来鞭尸,在多写一个分块题解。





原创粉丝点击