打卡 day 8
定义
在无向连通图中,如果将其中一个点以及所有连接该点的边去掉,图就不再连通,那么这个点就叫做割点。
在无向连通图中,如果将其中一条边去掉,图就不再连通,那么这条边叫桥。
LCA 最近公共祖先
定义完了开始解释题,有一个图,q次询问,每次会加一条边,问当前图中桥的数量。
哇哇哇,原来桥是这个意思,就是没了这条边不行,就不连通了。
缩点我上一道题已经弄明白了,说白了就是把一个环缩成一个点,因为环里面没有桥。。
dfn 时间戳
low 回溯时寻根
node结构体 为了实现链表
brige 当前节点是否为割点
vis 专门为lca准备的
一开始做tarjan,顺便把每个点的父节点都记录下来,为了LCA做准备。
同时用sum记录初始的图有多少个桥。
然后每次询问给两个点,我们就先用LCA找到他们的最近公共祖先,找到之后a和b分别通过找父节点一直找到那个祖先的位置,如果中途有的边是桥,因为相连后成环了,所以这条边就不算桥了,sum–,最后输出答案。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5+100;
int n,m;
int cnt,sum,num;
int dfn[maxn],low[maxn];
struct node{
int v,next;
}edge[maxn<<2];
int head[maxn],pre[maxn];
int vis[maxn],brige[maxn];
void init(){
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(brige,0,sizeof(brige));
sum=num=cnt=0;
}
void add(int u,int v){
edge[num].v=v;
edge[num].next=head[u];
head[u]=num++;
}
void tarjan(int u,int fa){
dfn[u]=low[u]=++cnt;
int f=1;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(v==fa&&f){
f=0;
continue;
}
if(!dfn[v]){
pre[v]=u;
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u]){
sum++;
brige[v]=1;
}
}else{
low[u]=min(low[u],dfn[v]);
}
}
}
int lca(int a,int b){
memset(vis,0,sizeof(vis));
while(b!=-1)
{
vis[b]=1;
b=pre[b];
}
while(!vis[a]) a=pre[a];
return a;
}
int main(){
int a,b,c,q;
int cn=0;
while(scanf("%d%d",&n,&m)){
if(n==0&&m==0) break;
init();
for(int i=0;i<m;i++){
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
pre[1]=-1;
tarjan(1,-1);
printf("Case %d:\n",++cn);
scanf("%d",&q);
while(q--){
scanf("%d%d",&a,&b);
c=lca(a,b);
while(a!=c){
if(brige[a]){
brige[a]=0;
sum--;
}
a=pre[a];
}
while(b!=c){
if(brige[b]){
brige[b]=0;
sum--;
}
b=pre[b];
}
printf("%d\n",sum);
}
}
return 0;
}