『追捕盗贼 Tarjan算法』
<更新提示>
<第一次更新>
<正文>
追捕盗贼(COCI2007)
Description
为了帮助警察抓住在逃的罪犯,你发明了一个新的计算机系统。警察控制的区域有N个城市,城市之间有E条双向边连接,城市编号为1到N。
警察经常想在罪犯从一个城市逃亡另一个城市的过程中抓住他。侦查员在仔细研究地图,以决定在哪个城市设置障碍,或者断掉某条路。你的计算机系统必须回答以下两种问题:
1、 如果连接城市G1和G2的路被封掉,罪犯能否从城市A逃到城市B?
2、 如果城市C被封掉,罪犯能否从城市A逃到城市B?
编程实现上述计算机系统。
Input Format
输入第一行包含两个整数N和E(2<=N<=100000,1<=E<=500000),表示城市和边的数量。
接下来E行,每行包含两个不同的整数Ai和Bi,表示城市Ai和Bi之间有一条边直接相连,任意两个城市之间最多只有一条边相连。
接下来一行包含一个整数Q(1<=Q<=300000),表示询问次数。
接下来Q行,每行包含4个或5个整数,第一个数为1或2,表示询问问题的种类。
如果问题种类是1,后面跟着4个整数A,B,G1,G2,如上描述表示询问如果G1和G2之间的路封掉罪犯能否从城市A逃到城市B,保证A和B不同,G1和G2之间一定存在路。
如果问题种类是2,后面跟着三个整数A,B,C,三个数互不相同,表示询问如果封掉城市C,罪犯能否从城市A逃到城市B。
输入数据保证一开始任意两个城市都可以相互到达。
Output Format
每个询问输出一行“yes”或“no”。
Sample Input
13 15
1 2
2 3
3 5
2 4
4 6
2 6
1 4
1 7
7 8
7 9
7 10
8 11
8 12
9 12
12 13
5
1 5 13 1 2
1 6 2 1 4
1 13 6 7 8
2 13 6 7
2 13 6 8
Sample Output
yes
yes
yes
no
yes
解析
这是一道\(Tarjan\)算法的综合运用题,考察了了有关割点割边等内容。
由于询问的数量较多,我们考虑离线的\(Tarjan\)算法,将原图化为一棵搜索树。
先看一下第一问,和割边的格式有些相似。我们先考虑封掉的边是不是割边。如果不是割边,则对连通性没有影响。如果是割边,我们可以利用如下方法判定。
设封掉的边连接的两个点为\(G1\),\(G2\)。(深度较深的为\(G1\))
1.点\(A\)在以\(G1\)为根的子树中,但点\(B\)不在以\(G1\)为根的子树中。
2.点\(B\)在以\(G1\)为根的子树中,但点\(A\)不在以\(G1\)为根的子树中。
由于一条割边只能将原图分为两个不同的联通块,所以当两个条件满足一个时,割边必然将\(A\),\(B\)两点分在了不同的联通块中。
割边我们可以在做\(Tarjan\)时求出,考虑如何判断某个点\(P1\)是否在以点\(P2\)为根的子树中。
我们重新记录一个值\(Maxdfn_x\)代表以x为根的子树中的最大\(dfn\)值。由于一个节点的\(dfn\)值必然比他的祖先节点的\(dfn\)值小,我们就可以利用\(Maxdfn\)数组得到\(subtree\)函数。
inline bool subtree(int p1,int p2)//代表p1是否在以p2为根的子树中
{
return dfn[p1]>=dfn[p2]&&dfn[p1]<=Maxdfn[p2];
}
一遍\(Tarjan\)就可以解决第一问,考虑如何解决第二问。
我们仍然要求\(C\)点为割点,由于一个割点会将图分为若干个连通分量,我们需要做特殊的记录。对于每一个\(2\)号询问,设\(yA\)代表点\(C\)到点\(A\)路径上的第一个点的编号,\(yB\)代表点\(C\)到点\(B\)路径上的第一个点的编号。
如果\(A\),\(B\)不在同一条路径上,即\(yA≠yB\),并且\(yA\)没有能够向上跳超过\(C\)点的返祖边,或者\(yB\)没有能够向上跳超过\(C\)点的返祖边\(B\),那么\(A\),\(B\)必然不能互达。
只要求出\(yA\),\(yB\)就可以了,可以用如下方法:
设\(askA_x\)代表对于某个\(2\)号询问中\(A\)点为\(x\)的询问编号,\(askB_x\)代表对于某个\(2\)号询问中\(B\)点为\(x\)的询问编号,\(askCa_x\)代表某个\(2\)号询问中\(A\)点要求封掉的点\(C\)编号为\(x\)的询问编号,\(askCb_x\)代表某个\(2\)号询问中\(B\)点要求封掉的点\(C\)编号为\(x\)的询问编号。
对于\(Tarjan\)访问到每一个点x时,我们将\(askA_x,askB_x\)一一取出,并以\(C\)节点作为下标将询问编号统计加入\(askCa,askCb\)数组中,在回溯到父节点时,我们就可以方便地统计得到每一个询问的\(yA\),\(yB\)值。
这样问题就得到了解决。
\(Code:\)
#include<bits/stdc++.h>
using namespace std;
const int N=100000+200,E=500000+200,Q=300000+200;
int n,m,q,Last[E*2],t,dfn[N],low[N],Maxdfn[N],cnt=0,yA[Q],yB[Q];
struct edge{int ver,next;}e[E*2];
struct question{int a,b,c,x,y,index;}ques[Q];
vector < int > askA[N],askB[N],askCa[N],askCb[N];
inline void insert(int x,int y)
{
e[++t].ver=y;e[t].next=Last[x];Last[x]=t;
}
inline void input(void)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
insert(u,v);
insert(v,u);
}
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
int index;
scanf("%d",&ques[i].index);
if(ques[i].index==1)
scanf("%d%d%d%d",&ques[i].a,&ques[i].b,&ques[i].x,&ques[i].y);
if(ques[i].index==2)
{
scanf("%d%d%d",&ques[i].a,&ques[i].b,&ques[i].c);
askA[ques[i].a].push_back(i);
askB[ques[i].b].push_back(i);
}
}
}
inline void Tarjan(int x,int fa)
{
dfn[x]=low[x]=++cnt;
while(askA[x].size())
{
int num=askA[x].back();
if(dfn[ques[num].c])
askCa[ques[num].c].push_back(num);
askA[x].pop_back();
}
while(askB[x].size())
{
int num=askB[x].back();
if(dfn[ques[num].c])
askCb[ques[num].c].push_back(num);
askB[x].pop_back();
}
for(int i=Last[x];i;i=e[i].next)
{
int y=e[i].ver;
if(y==fa)continue;
if(!dfn[y])
{
Tarjan(y,x);
low[x]=min(low[x],low[y]);
while(askCa[x].size())
{
yA[askCa[x].back()]=y;
askCa[x].pop_back();
}
while(askCb[x].size())
{
yB[askCb[x].back()]=y;
askCb[x].pop_back();
}
}
else low[x]=min(low[x],dfn[y]);
}
Maxdfn[x]=cnt;
}
inline bool subtree(int p1,int p2)
{
return dfn[p1]>=dfn[p2]&&dfn[p1]<=Maxdfn[p2];
}
inline void answer(void)
{
for(int i=1;i<=q;i++)
{
if(ques[i].index==1)
{
if(ques[i].x==ques[i].y)printf("yes\n");
else
{
if(dfn[ques[i].x]<dfn[ques[i].y])swap(ques[i].x,ques[i].y);
if( (low[ques[i].x]==dfn[ques[i].x]) && (subtree(ques[i].a,ques[i].x)^subtree(ques[i].b,ques[i].x)) )
printf("no\n");
else printf("yes\n");
}
}
if(ques[i].index==2)
{
if(yA[i]==yB[i])
printf("yes\n");
else
if( (yA[i]&&low[yA[i]]>=dfn[ques[i].c]) || (yB[i]&&low[yB[i]]>=dfn[ques[i].c]) )
printf("no\n");
else printf("yes\n");
}
}
}
int main(void)
{
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
input();
for(int i=1;i<=n;i++)
if(!dfn[i])Tarjan(i,0);
answer();
return 0;
}
<后记>
『追捕盗贼 Tarjan算法』的更多相关文章
- 【算法•日更•第二十八期】图论:强连通+Tarjan算法(一)
▎前言 一直都想学习这个东西,以为很难,结果发现也不过如此. 只要会些图论的基础就可以了. ▎强连通 ☞『定义』 既然叫强连通,那么一定具有很强的连通性. 强连通:就是指在一个有向图中,两个顶点可以互 ...
- 『Tarjan算法 无向图的双联通分量』
无向图的双连通分量 定义:若一张无向连通图不存在割点,则称它为"点双连通图".若一张无向连通图不存在割边,则称它为"边双连通图". 无向图图的极大点双连通子图被 ...
- 『Tarjan算法 有向图的强连通分量』
有向图的强连通分量 定义:在有向图\(G\)中,如果两个顶点\(v_i,v_j\)间\((v_i>v_j)\)有一条从\(v_i\)到\(v_j\)的有向路径,同时还有一条从\(v_j\)到\( ...
- 『Tarjan算法 无向图的割点与割边』
无向图的割点与割边 定义:给定无相连通图\(G=(V,E)\) 若对于\(x \in V\),从图中删去节点\(x\)以及所有与\(x\)关联的边后,\(G\)分裂为两个或以上不连通的子图,则称\(x ...
- 『图论』有向图强连通分量的Tarjan算法
在图论中,一个有向图被成为是强连通的(strongly connected)当且仅当每一对不相同结点u和v间既存在从u到v的路径也存在从v到u的路径.有向图的极大强连通子图(这里指点数极大)被称为强连 ...
- 重新学习MySQL数据库2:『浅入浅出』MySQL 和 InnoDB
重新学习Mysql数据库2:『浅入浅出』MySQL 和 InnoDB 作为一名开发人员,在日常的工作中会难以避免地接触到数据库,无论是基于文件的 sqlite 还是工程上使用非常广泛的 MySQL.P ...
- 『浅入浅出』MySQL 和 InnoDB
作为一名开发人员,在日常的工作中会难以避免地接触到数据库,无论是基于文件的 sqlite 还是工程上使用非常广泛的 MySQL.PostgreSQL,但是一直以来也没有对数据库有一个非常清晰并且成体系 ...
- 『TensorFlow2.0正式版』TF2.0+Keras速成教程·零:开篇简介与环境准备
此篇教程参考自TensorFlow 2.0 + Keras Crash Course,在原文的基础上进行了适当的总结与改编,以适应于国内开发者的理解与使用,水平有限,如果写的不对的地方欢迎大家评论指出 ...
- 有向图强连通分量的Tarjan算法
有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G ...
随机推荐
- UOJ#41. 【清华集训2014】矩阵变换 构造
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ41.html 题解 首先写个乱搞: 一开始每一行都选择第一个非0元素,然后,我们对这个方案不断做更新,直到 ...
- RabbitMQ中RPC的实现及其通信机制
RabbitMQ中RPC的实现:客户端发送请求消息,服务端回复响应消息,为了接受响应response,客户端需要发送一个回调队列的地址来接受响应,每条消息在发送的时候会带上一个唯一的correlati ...
- hashTabel List 和 dic
hashTabel List 和 dic 原:https://www.cnblogs.com/jilodream/p/4219840.html .Net 中HashTable,HashMap 和 ...
- jdk1.8安装
jdk1.8:链接: https://pan.baidu.com/s/1Orv7Rjz0jkprcdoRSFWiPw 提取码: rn73 设置jdk和jre文件位置 配置环境变量: 系统变量中jdk ...
- MyIbatis和Hibernate的区别--2019-04-26
1.MyBatis 真正实现了java代码和sql的分离 2.Hibernate 是全自动的,MyBatis是半自动的 Hibernate实现了部分自动生成SQL 3.SQL优化上 MyBatis 强 ...
- GitHub的简单使用记录
记录于:2013/4/24 GitHub(网址 https://github.com/)是一个面向开源及私有软件项目的托管平台,因为只支持Git作为唯一的版本库格式进行托管,故名GitHub. G ...
- 第三方布局框架Neon初探
github地址:https://github.com/mamaral/Neon 居中 设置 view 在 superview 的中心,调用 anchorInCenter()并设置view大小,相当于 ...
- Big big world
Big big world I'm a big big girl 我是个大女孩 in a big big world 在这个大大的世界上 It's not a big big thing if you ...
- NOIP2017 国庆郑州集训知识梳理汇总
第一天 基础算法&&数学 day1难度测试 如果要用一个词来形容上午的测试,那真是体无完肤. 成绩: 题目 成绩 评价 T1 50 一般 T2 10 大失所望 T3 0 差 基础算法 ...
- windows下编译Boost
当前boost最新版本为1.55,下载地址:http://sourceforge.net/projects/boost/files/boost/1.55.0/或者从官网(www.boost.org)下 ...