POJ2892_Tunnel Warfare_solution
来源:互联网 发布:网络限速设置多少合适 编辑:程序博客网 时间:2024/05/29 03:18
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 nextm 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
Hint
An illustration of the sample input:
OOOOOOOD 3 OOXOOOOD 6 OOXOOXOD 5 OOXOXXOR OOXOOXOR OOXOOOO
Source
#include<stdio.h>#include<string.h>#include <set>using namespace std;int main (){int n,m,deletedStack[50005],stack_top;int i,p,lower,up;bool deleted[50005];//标记某个节点是否删除,已经删除的节点我们特殊处理,直接输出0char cmd[10];set<int>::iterator itlower,itup;//集合的迭代器,用来查上界和下界set<int> deletedPoint; //删除的点的有序集合while(scanf("%d%d",&n,&m)!=EOF){stack_top=0;//初始被删除的元素为0个,栈为空memset(deleted,0,sizeof(deleted));//没有元素被删除deletedPoint.insert(0);//集合中开始加入0,n+1两个元素deletedPoint.insert(n+1);for(i=1;i<=m;i++){scanf("%s",&cmd);if(cmd[0]=='D')//摧毁某个村庄{scanf("%d",&p);deletedStack[++stack_top]=p;//入栈deletedPoint.insert(p);//加入被删除元素的有序集合deleted[p]=true;//被删除}else if(cmd[0]=='R')//重建栈顶村庄{p=deletedStack[stack_top--];//出栈deletedPoint.erase(p);//从集合删除deleted[p]=false;//恢复}else //询问{scanf("%d",&p);if(deleted[p])//被删除的村庄直接输出0{printf("0\n");continue;}deletedPoint.insert(p);//先将要查询的村庄加入有序集合,因为这样才能查询itlower=itup=deletedPoint.find(p);//再记录下要查询的村庄所在位置的迭代器itlower--;//下一位就是恰好比它小的被摧毁村庄,即相连村庄的下界lower=*itlower;itup++;//往上一位就是恰好比它大的被摧毁的村庄,即相连村庄的上界up=*itup;deletedPoint.erase(p);//查询完毕,把查询的村庄删除printf("%d\n",up-1-lower);}}deletedPoint.clear();//清空集合,以备下一个testCase使用}return 0;}
下面再来说说怎么使用线段树解这个题目,其实我们查询某个点的时候就是查询这个点所在的最大连续区间,我们要记录的就是一些连续区间的起点和终点。但是如果直接去记录起点和终点,那也不好操作,比如一个区间很多被删除的点。我们把一个区间一分为二,并且记录以左端点开始的最大相连村庄长度,以及以右端点结束的最大长度。上个图来说明一下:
父节点start_left_len=左子结点start_left_len;父节点end_right_len=右子结点end_right_len;父节点max_len=max(左子结点max_len,右子结点max_len,左子结点end_right_len+右子结点start_left_len);If 左子结点是完全相连的区间,即max_len=right-left+1 父节点start_left_len+=右子结点start_left_len;If 右子结点是完全相连的区间,即max_len=right-left+1 父节点end_right_len+=左子结点end_right_len;
对照上面画的图,自己在画画图就很清楚了。
#include<stdio.h>#include<string.h>#define MAXN 50005int n,m,stack[MAXN],stack_top;//stack用来存储删除的节点,stack_top=0时stack为空struct segTreeInfo//线段树结构体{int left,right;//管理区间int start_left_len;//以left开始的相连节点个数int end_right_len;//以right结束的相连节点个数int max_len;//left至right整个区间出现的最长相连节点个数};segTreeInfo segTree[3*MAXN];int getMax(int a,int b){return a>b?a:b;}void buildTree(int pos,int left,int right){int len=right-left+1;segTree[pos].left=left;segTree[pos].right=right;segTree[pos].start_left_len=len;//初始状态长度是整个管理区间segTree[pos].end_right_len=len;segTree[pos].max_len=len;if(left==right){return;}int mid=(left+right)/2;buildTree(pos<<1,left,mid);buildTree(pos<<1|1,mid+1,right);}void update(int pos,int destination,int operation)//destination表示要更新的节点,operation=1表示rebuild,operation=0表示destroy{int left=segTree[pos].left;int right=segTree[pos].right;if(left==right&&destination==left)//找到叶子节点才开始更新{segTree[pos].start_left_len=operation;//rebuild变为1,然后开始往上回溯更新父节点segTree[pos].end_right_len=operation;//destroy变为0,然后开始往上回溯更新父节点segTree[pos].max_len=operation;return;}int mid=(left+right)/2;if(destination<=mid)update(pos<<1,destination,operation);elseupdate(pos<<1|1,destination,operation);segTree[pos].start_left_len=segTree[pos<<1].start_left_len;//以left开始的长度=左孩子以left开始的长度segTree[pos].end_right_len=segTree[pos<<1|1].end_right_len;//以right结束的长度=右孩子以right结束的长度segTree[pos].max_len=getMax(segTree[pos<<1].end_right_len+segTree[pos<<1|1].start_left_len,getMax(segTree[pos<<1|1].max_len,segTree[pos<<1].max_len));/*上面有点长的这条语句好混乱,就是从三个数里取最大值,首先是max_len=getMax(左孩子能形成的最大长度,右孩子能形成的最大长度)因为mid和mid+1这两个节点可能是相连的,所以这个长度就是(左孩子以mid结尾的长度+右孩子以mid+1开始的长度),用这个长度再去更新max_len就是最终的max_len*/if(segTree[pos<<1].start_left_len==mid-left+1)//左孩子是完全相连的串,即没有被摧毁的村庄{segTree[pos].start_left_len+=segTree[pos<<1|1].start_left_len;//那么父节点以left开始的长度需要加上右孩子最左段长度}if(segTree[pos<<1|1].end_right_len==right-(mid+1)+1)//右孩子是完全相连的串{segTree[pos].end_right_len+=segTree[pos<<1].end_right_len;//那么父节点以right结尾的长度需要加上左孩子最右段的长度}}int query(int pos,int destination)//查询与destination相连的村庄数量{int left=segTree[pos].left,right=segTree[pos].right;/*在这些情况下返回:到达叶子节点;到达的这段区间所有村庄被摧毁;到达的这段区间所有村庄都相连*/if(left==right||segTree[pos].max_len==0||segTree[pos].max_len==right-left+1)return segTree[pos].max_len;/*这里处理的情况即是这段区间有被摧毁的村庄,这段区间包含要查找的目标destination*/int mid=(left+right)/2;if(destination<=mid)//destination落在左孩子管理区间{if(destination>=mid-segTree[pos<<1].end_right_len+1)//如果destination落在左孩子管理的最右段return query(pos<<1,destination)+query(pos<<1|1,mid+1);//那么与destination相连的村庄长度还要加上右孩子的最左段else//不落在左孩子管理的最右段,那么与destination相连的长度只和左孩子管理的区间相关return query(pos<<1,destination);}else//destination落在右孩子管理区间{if(destination<=mid+segTree[pos<<1|1].start_left_len)//如果destination落在右孩子的最左段return query(pos<<1,mid)+query(pos<<1|1,destination);//那么与destination相连的村庄长度还要加上左孩子的最右段else//不落在右孩子的最左段,那么与destination相连的长度只和右孩子管理的区间相关return query(pos<<1|1,destination);}}int main(){int i,destination;char cmd[5];while(scanf("%d%d",&n,&m)!=EOF){//n=村庄数量,m=操作命令数量stack_top=0;//初始时没摧毁任何村庄,栈为空buildTree(1,1,n);//建立管理n个村庄的线段树for(i=1;i<=m;i++)//顺序处理m个命令{scanf("%s",&cmd);if(cmd[0]=='D')//摧毁村庄{scanf("%d",&destination);stack[++stack_top]=destination;//被摧毁的村庄入栈update(1,destination,0);//摧毁destination,更新线段树,后面的0表示摧毁}else if(cmd[0]=='R')//重建村庄{destination=stack[stack_top--];//从栈中取出一个村庄进行重建update(1,destination,1);//重建destination,更新线段树,后面的1表示重建}else//询问与destination相连的村庄数量{scanf("%d",&destination);printf("%d\n",query(1,destination));}}}return 0;}
- POJ2892_Tunnel Warfare_solution
- qt-everywhere-opensource-src-4.8.4 编译出错
- Android源码中如何添加apk文件(源码预安装apk)
- 130826触发器
- RichEdit的加载问题
- 安卓开发---设置活动栏
- POJ2892_Tunnel Warfare_solution
- Android VLC播放器二次开发1——程序结构分析
- 用TinyXml做XML解析示例 TinyXml查找唯一节点及修改节点操作
- java中list、set和map 的区别
- iOS开发之获取当前系统的语言环境
- js的数据类型
- 如何用SQL Server来自动发送邮件?
- 大家知道Jquery中有serialize方法
- 判断ios中是否安装了某些软件