题意

给定无向图,给出 q q q个询问

​传送门​

给定 ( u , v ) (u,v) (u,v),问删去某条边是否还连通,删去某个点是否还连通

解析

关于连通性,直接上 t a r j a n tarjan tarjan制造一颗 d f s dfs dfs的搜索树

对 于 ( c , d ) 边 \color{Red}对于(c,d)边 对于(c,d)边

首先如果 ( c , d ) (c,d) (c,d)不是桥,对连通性无影响

如果是桥,删去后图会分成两半,一半含 c c c一半含 d d d

令 c c c是搜索树中深度大的那个节点

若 a ( b ) a(b) a(b)在 c c c的子树内且 b ( a ) b(a) b(a)不在 c c c的子树内就对连通性有影响

而判断 v v v在 u u u的子树内就是

d f n [ v ] > d f n [ u ] & & f i n a l [ v ] < f i n a l [ u ] dfn[v]>dfn[u]\&\&final[v]<final[u] dfn[v]>dfn[u]&&final[v]<final[u]

其中 f i n a l final final记录的是离开点 x x x时的序号.

对 于 一 点 c \color{Red}对于一点c 对于一点c

Ⅰ.若 a , b a,b a,b不在 c c c的子树内,去掉点 c c c对连通性不会有影响

Ⅱ.若 a , b a,b a,b都在 c c c的子树内,我们可以先把 a , b a,b a,b提升到 c c c的儿子位置

若此时 a = = b a==b a==b,说明在同一个分枝,不影响连通性

否则, a , b a,b a,b无法在 c c c子树内相会,那么去判断是否能在 c c c子树外相遇

也就是判断 l o w [ a ] < d f n [ c ] & & l o w [ b ] < d f n [ c ] low[a]<dfn[c]\&\&low[b]<dfn[c] low[a]<dfn[c]&&low[b]<dfn[c]

这样就是可以到更浅的节点去相遇

Ⅲ.一点在 c c c子树内,一点不在.判断同上,看看子树内的能不能跑到子树外去相会

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
struct edge{
int to,nxt;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v){ d[++cnt] = (edge){v,head[u]},head[u] = cnt; }
int low[maxn],dfn[maxn],deep[maxn],final[maxn],id,top;
int fa[maxn][21],n,m;
void tarjan(int u,int FA,int depth)
{
low[u] = dfn[u] = ++id, deep[u] = depth;
int flag = 0;
for(int i=head[u];i;i=d[i].nxt )
{
int v = d[i].to;
if( !dfn[v] )
{
tarjan( v,u,depth+1 );
fa[v][0] = u;
low[u] = min( low[u],low[v] );
}
else
{
if( v!=FA||flag ) low[u] = min( low[u],dfn[v] );
else flag = 1;
}
}
final[u] = id;
}
bool isok_bian(int a,int b,int c,int d)//如果去掉边(c,d),a也能到b,返回true
{
if( deep[c]<deep[d] ) swap(c,d);//保证c是下面的那个点
bool son_a=( dfn[a]>=dfn[c]&&final[a]<=final[c] );//a在c子树内
bool son_b=( dfn[b]>=dfn[c]&&final[b]<=final[c] );//b在c子树内
if( low[c]>dfn[d]&&(son_a^son_b) ) return 0;//(c,d)是桥且一个在c内一个不在
return 1;
}
int k_th(int x,int step)
{
if( step<0 ) return -1;
for(int i=20;i>=0;i--) if( (1<<i)&step ) x=fa[x][i];
return x;
}
bool isok_dian(int a,int b,int c)
{
bool son_a=( dfn[a]>=dfn[c]&&final[a]<=final[c] );//a在c子树内
bool son_b=( dfn[b]>=dfn[c]&&final[b]<=final[c] );//b在c子树内
if( !son_a&&!son_b ) return true;//都不在子树内,不影响
else if( son_a&&!son_b )//a在子树内,b不在
{
int x = k_th( a,deep[a]-deep[c]-1 );
if( x!=-1&&low[x]<dfn[c] ) return true;
}
else if( !son_a&&son_b )
{
int x = k_th( b,deep[b]-deep[c]-1 );
if( x!=-1&&low[x]<dfn[c] ) return true;
}
else//都在c子树内
{
int aa=k_th( a,deep[a]-deep[c]-1 );
int bb=k_th( b,deep[b]-deep[c]-1 );
if( aa==bb ) return true;//同一个分枝,不影响
if( low[aa]<dfn[c]&&low[bb]<dfn[c] ) return true;//都能到更浅的节点相聚
}
return false;
}
void init()
{
for(int i=0;i<=n;i++)
low[i]=dfn[i]=final[i]=deep[i]=head[i]=0;
cnt=1,id=top=0;
}
int main()
{
while( cin >> n >> m )
{
for(int i=1;i<=m;i++)
{
int l,r; scanf("%d%d",&l,&r);
add(l,r); add(r,l);
}
for(int i=1;i<=n;i++) if( !dfn[i] ) tarjan(i,0,0);
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++)//倍增
fa[j][i] = fa[fa[j][i-1]][i-1];
int q; scanf("%d",&q);
while( q-- )
{
int type,a,b,c,d,flag=0;
scanf("%d",&type);
if( type==1 )//询问边来了
{
scanf("%d%d%d%d",&a,&b,&c,&d);
if( isok_bian(a,b,c,d) ) flag = 1;
}
else//询问点来了
{
scanf("%d%d%d",&a,&b,&c);
if( isok_dian(a,b,c) ) flag = 1;
}
cout << (flag?"yes\n":"no\n");
}
init();
}
}