题意:给出一个无向图,然后多次询问,每次添加一条有向边后该图中存在多少桥。
用tarjan算法统计有多少个桥,并将这些边标记,对于每次询问,求两个点的LCA,在寻找的途中路过的边全都不是桥,所以若遇到有标记的边,则计数减一并取消标记即可(避免多次计数)。可以直接利用tarjan函数中的dfn数组来求LCA,因为dfn记录的就是每个点的访问次序。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 205000
#define M 405000
int dfn[N],low[N],belong[N],f[N];
bool instack[N],bridge[M];
int head[N];
int k,cnt,num;
int n,m;
struct edge
{
int u,v,next;
} e[M];
void add(int u,int v)
{
e[k].u=u;
e[k].v=v;
e[k].next=head[u];
head[u]=k++;
}
void tarjan(int u,int id)
{
dfn[u]=low[u]=++num;
instack[u]=true;
for(int i=head[u]; i!=-1; i=e[i].next)
{
if(i==(1^id)) continue;
int v=e[i].v;
if(!dfn[v])
{
f[v]=u;
tarjan(v,i);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u])
{
cnt++;
bridge[v]=true;
}
}
else if(instack[v])
low[u]=min(low[u],dfn[v]);
}
}
void init()
{
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(instack,false,sizeof(instack));
memset(bridge,false,sizeof(bridge));
for(int i=1; i<=n; i++) f[i]=i;
k=cnt=num=0;
}
void LCA(int u,int v)
{
if(u==v) return;
else
{
if(dfn[u]>dfn[v])
{
if(bridge[u])
bridge[u]=false,cnt--;
LCA(f[u],v);
}
else if(dfn[v]>dfn[u])
{
if(bridge[v])
bridge[v]=false,cnt--;
LCA(u,f[v]);
}
}
}
int main()
{
int u,v,q,t=1;
while(scanf("%d%d",&n,&m),n+m)
{
init();
while(m--)
{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
for(int i=1; i<=n; i++)
{
if(!dfn[i])
tarjan(i,-1);
}
printf("Case %d:\n",t++);
scanf("%d",&q);
while(q--)
{
scanf("%d%d",&u,&v);
LCA(u,v);
printf("%d\n",cnt);
}
printf("\n");
}
}