题目链接:点击打开链接
题目给定一张无向连通图,q个询问,问删除指定某条边或者某个点后,给定两点是否连通。
首先,对于删除边的情况进行讨论,如果删去的边不为割边,那么删去这条边图仍然连通;如果该边是割边,那么只有在给定两点恰好在割边两边,那么删除这条边后两点不连通。在dfs生成树上就是表示为给定的两个点,其中一点为割边下端点的儿子,另一点不是,则删去该边两点不连通。
其次,对于删除点的情况进行讨论,分三种情况讨论:第一种,删去点不为两点任意一点的祖先,那么删去该点后,对于两点连通性没有影响;第二种,其中一个点为删去点的儿子,另一个点不是,那么这个时候无法通过经过祖先结点的路径使该两点连通,所以对于为后代的那个点,必须绕过删去点到达删去点的祖先,这样两个点才能连通。结合Low[]数组的含义,Low[u]表示的是不从树边(祖先边)过来,该节点和其后代结点能追溯到的最早的结点~~~所以对于这种情况,只需要判断为后代的那个点距离删去点最近的祖先的Low[u]是否小于删去点的dfn[v],如果成立,那么删去该节点后两点仍然连通。第三种情况,给定两个点都为删去点的后代,那么如果两个点距离删去点最近的祖先相同,那么删去该点后两点仍然连通,否则必须通过后向边追溯上去(同上)~
附上代码~~~~
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN=101000;
const int MAXM=1001000;
struct edge_t
{
int to,next;
}edge[MAXM];
int head[MAXN],tot;
int Low[MAXN],DFN[MAXN],fin[MAXN],dep[MAXN];
int anc[MAXN][20];
//anc[u][i]记录的是u的第2^i个祖先
int Index,fx;
//这里Index记录的是dfs生成树上进入该节点的时间戳,fx指的是离开该节点的时间戳,用于判断是否为祖先关系
bool bridge[MAXN];
//bridege这里记录的是dfs生成树上从该节点父节点到该节点的这条边是否为割边
inline void init()
{
tot=0;
memset(head,-1,sizeof(head));
}
inline void addedge(int u,int v)
{
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void tarjan(int u,int pre)
{
int v;
anc[u][0]=pre;
dep[u]=dep[pre]+1;
Low[u]=DFN[u]=++Index;
bool first=true;
for(int i=head[u];i!=-1;i=edge[i].next)
{
v=edge[i].to;
if(v==pre&&first)
{
first=false;
continue;
}
if(!DFN[v])
{
tarjan(v,u);
if(Low[u]>Low[v]) Low[u]=Low[v];
if(Low[v]>DFN[u]) bridge[v]=true;
//u->v这条边为割边
}
else if(Low[u]>DFN[v])
Low[u]=DFN[v];
}
fin[u]=++fx;
}
void solve(int n)
{
memset(DFN,0,sizeof(DFN));
memset(bridge,false,sizeof(bridge));
Index=fx=0;
memset(anc,0,sizeof(anc));
for(int i=1;i<=n;i++)
if(!DFN[i]) tarjan(i,0);
}
//u是v的儿子
inline bool isson(int u,int v)
{
return DFN[u]>=DFN[v]&&fin[u]<=fin[v];
}
int find(int u,int h)
{
int k=0;
while(h)
{
if(h&1) u=anc[u][k];
h>>=1;
k++;
}
return u;
}
//判断c是否为a,b之间的割点
bool judgevertex(int a,int b,int c)
{
bool in1=isson(a,c);
bool in2=isson(b,c);
if(in1&in2)
{
int anc1=find(a,dep[a]-dep[c]-1);
int anc2=find(b,dep[b]-dep[c]-1);
if(anc1==anc2) return true;
return Low[anc1]<DFN[c]&&Low[anc2]<DFN[c];
}
if(in1^in2)
{
if(!in1) swap(a,b);
int ancs=find(a,dep[a]-dep[c]-1);
return Low[ancs]<DFN[c];
}
return true;
}
int main()
{
int n,m,q;
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
int u,v;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
solve(n);
for(int j=1;(1<<j)<n;j++)
for(int i=1;i<=n;i++)
if(anc[i][j-1]) anc[i][j]=anc[anc[i][j-1]][j-1];
scanf("%d",&q);
while(q--)
{
int op,a,b,c,d;
bool flag=true;
scanf("%d",&op);
if(op==1)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
if(dep[c] < dep[d]) swap(c, d);
int in1 = isson(a, c);
int in2 = isson(b, c);
if(dep[d]+1==dep[c]&&isson(c,d)&&bridge[c] && (in1 ^ in2) == 1) flag = false;
}
else
{
scanf("%d%d%d",&a,&b,&c);
flag=judgevertex(a,b,c);
}
puts(flag?"yes":"no");
}
}
return 0;
}