连通分量
定义
强连通分量是指有向图的一个极大子图使得其中每个点都可以到达其他所有点。
双连通分量是指无向图中的一个极大子图,分为两种:
- 点双连通分量:至少删去两个点才能使子图不连通
- 边双连通分量:至少删去两条边才能使子图不连通
图的连通性问题一般都有对应的Tarjan算法求解,一个著名的例子就是支配树的Lengauer-Tarjan算法。这里要讲的是连通分量的Tarjan算法。
Tarjan算法
Tarjan算法基于dfs搜索树。定义\(dfn[x]\)为\(x\)被搜索的时间,再定义一个\(low[x]\),表示\(x\)能绕回的祖先中\(dfn\)值最小的那个。\(low\)数组的求解是十分简单的。对于树枝边,并不会绕回到祖先去,所以\(low[x]=min(low[x],low[v])\);对于后向边,\(low[x]=min(low[x],dfn[v])\)。
强连通分量
每搜索一个点,我们都把它加进栈里。如果搜索完\(x\)的所有连边后发现\(dfn[x]=low[x]\),那么它与当前在栈内的所有点构成了一个强连通分量。
双连通分量
点双连通分量
深搜的过程中,我们把边放进栈中,如果搜到的是后向边,那么更新当前点的\(low\)值。如果是树边,那么搜完之后,若\(dfn[u]\le low[v]\)即从下面无法走回到当前点之前的点,那么栈中之前压入的边和上面的边包含的点就组成了一个点双连通分量。
边双连通分量
深搜的过程中,如果搜完一条树枝边后发现\(dfn[u]<low[v]\),那么这条边就是原图的一个桥。把全部桥搜出来后断掉它们,现在的每个连通图都是一个边双连通分量。
双连通分量代码
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int maxn=3e5+1;
const int maxm=1e6+1;
const int maxp=12;
struct edge {
int v,nxt;
} e[maxm];
int h[maxn],tot=1;
bool no[maxm],bridge[maxm];
void add(int u,int v) {
e[++tot]=(edge){v,h[u]};
h[u]=tot;
}
int point[maxn][maxp];
int pc=0,ege[maxn],ec=0,dfn=0,first[maxn],low[maxn];
int stap[maxn],topp=0;
struct bian {
int u,v;
bian (int u=0,int v=0):u(u),v(v) {}
} stae[maxm];
int tope=0;
bool ins[maxn];
void Tarjan(int x) {
first[x]=low[x]=++dfn;
stap[++topp]=x;
ins[x]=true;
for (int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) if (!no[i]) {
no[i]=no[i^1]=true;
if (!first[v]) {
stae[++tope]=bian(x,v);
Tarjan(v);
low[x]=min(low[x],low[v]);
if (first[x]<=low[v]) {
bian out;
++pc;
do {
out=stae[tope--];
int i;
for (i=1;i<=point[out.u][0];++i) if (point[out.u][i]==pc) break;
if (i>point[out.u][0]) ++point[out.u][0];
point[out.u][i]=pc;
for (i=1;i<=point[out.v][0];++i) if (point[out.v][i]==pc) break;
if (i>point[out.v][0]) ++point[out.v][0];
point[out.v][i]=pc;
} while (!(out.u==x && out.v==v));
}
if (first[x]<low[v]) {
bridge[i]=bridge[i^1]=true;
}
} else if (ins[v]) low[x]=min(low[x],first[v]);
}
ins[x]=false;
}
void dfs(int x) {
ege[x]=ec;
for (int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) if (!no[i] && !bridge[i]) {
no[i]=no[i^1]=true;
dfs(v);
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
int n=read(),m=read(),q=read();
for (int i=1;i<=m;++i) {
int u=read(),v=read();
add(u,v),add(v,u);
}
for (int i=1;i<=n;++i) if (!first[i]) {
Tarjan(i);
if (!point[i][0]) point[i][++point[i][0]]=++pc;
}
memset(no,0,sizeof no);
for (int i=1;i<=n;++i) if (!ege[i]) {
++ec,dfs(i);
}
while (q--) {
int op=read(),x=read(),y=read();
if (op==2) puts(ege[x]==ege[y]?"yes":"no"); else
if (op==1) {
bool flag=false;
for (int i=1,j=1;i<=point[x][0];++i) {
while (j<point[y][0] && point[y][j]<point[x][i]) ++j;
if (point[x][i]==point[y][j]) {
puts("yes");
flag=true;
break;
}
}
if (!flag) puts("no");
}
}
return 0;
}