题目链接
https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1076
题意
给出一个无向图G的顶点V和边E。进行Q次查询,查询从G的某个顶点V[s]到另一个顶点V[t],是否存在2条不相交的路径。(两条路径不经过相同的边)
(注,无向图中不存在重边,也就是说确定起点和终点,他们之间最多只有1条路)
题解
要想从u到v有两条不相交的路,那么这两条路上的边不能有桥。
因为删掉一条路径上的边不影响连通性。
也就是说u和v在一个边双连通分量上。
求边双连通分量需要先求出桥,然后将桥割掉,剩下的连通分量就是边双连通分量。
求桥的话,dfs即可。
dfs确定时间戳dfn和low,dfn表示自然遍历顺序,low[v]表示能通过其他边(u是v的父节点,除了v-u这条边)到达的dfn最小的点的dfn值。
对于边u-v,u是v的父节点,
如果low[v]>dfn[u],那么u-v是桥。
debug:
无向图是双向边,一开始isBridge数组空间开小了。结果最后三组数据WA了。
神特么报错。
AC代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=5e4+7;
struct edge
{
int to,next;
}e[maxn<<1];//双向边,开两倍空间
int head[maxn],cnt;
void init()
{
memset(head,-1,sizeof(head));
cnt=-1;
}
void add_edge(int u,int v)
{
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
int low[maxn],dfn[maxn],tot;
bool isBridge[maxn<<1]; //双向边,开两倍空间
void dfs(int u,int fa)
{
low[u]=dfn[u]=++tot;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(v==fa) continue;
if(dfn[v]) low[u]=min(low[u],dfn[v]);
else
{
dfs(v,u);
low[u]=min(low[u],low[v]);
}
if(low[v]>dfn[u])
{
isBridge[i]=isBridge[i^1]=true;
// printf("bridge: %d %d\n",u,v);
}
}
}
bool vis[maxn];
int belong[maxn];
void dfs2(int u,int x)
{
vis[u]=true;
belong[u]=x;
//printf("war: %d %d\n",u,belong[u]);
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(vis[v]) continue;
if(!isBridge[i])
dfs2(v,x);
}
}
int main()
{
int m,n;
while(~scanf("%d%d",&m,&n))
{
init();
for(int i=1;i<=n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
memset(isBridge,false,sizeof(isBridge));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
tot=0;
for(int i=1;i<=m;i++)
if(!dfn[i]) dfs(i,0);
memset(vis,false,sizeof(vis));
memset(belong,0,sizeof(belong));
for(int i=1;i<=m;i++)
{
if(!vis[i])
{
dfs2(i,i);
}
}
int q;
scanf("%d",&q);
while(q--)
{
int u,v;
scanf("%d%d",&u,&v);
//printf("belong: %d %d\n",belong[u],belong[v]);
if(belong[u]==belong[v]) printf("Yes\n");
else printf("No\n");
}
}
return 0;
}