#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,ans;
int deep[200005],low[200005],fa[200005],pre[200005],tim;
vector<int> G[200005];
int find(int x)
{
if(pre[x]==x)
{
return x;
}
return pre[x]=find(pre[x]);
}
bool unite(int x,int y)
{
int fx=find(x);
int fy=find(y);
if(fx==fy)
{
return false;
}
pre[fx]=fy;
return true;
}
void tarjan(int x,int father)
{
fa[x]=father;
deep[x]=low[x]=++tim;
for(int i=0;i<G[x].size();i++)
{
int y=G[x][i];
if(y==father)
{
continue;
}
if(deep[y]==0)
{
tarjan(y,x);
low[x]=min(low[x],low[y]);
if(deep[x]<low[y])
{
ans++;
}
else
{
unite(x,y);
}
}
else
{
low[x]=min(low[x],deep[y]);
}
}
}
int lca(int x,int y)
{
if(deep[x]<deep[y])
{
int tmp=x;
x=y;
y=tmp;
}
while(deep[x]>deep[y])
{
if(unite(x,fa[x]))
{
ans--;
}
x=fa[x];
}
while(x!=y)
{
if(unite(y,fa[y]))
{
ans--;
}
y=fa[y];
}
return ans;
}
int main()
{
int cae=1;
while(scanf("%d%d",&n,&m)&&!(n==0&&m==0))
{
for(int i=0;i<=100004;i++)
{
G[i].clear();
}
memset(deep,0,sizeof(deep));
memset(low,0,sizeof(low));
memset(fa,0,sizeof(fa));
memset(pre,0,sizeof(pre));
tim=ans=0;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
for(int i=1;i<=n;i++)
{
pre[i]=i;
}
tarjan(1,1);
int q;
scanf("%d",&q);
printf("Case %d:\n",cae);
for(int i=1;i<=q;i++)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",lca(x,y));
}
printf("\n");
cae++;
}
return 0;
}
对于一个环来说是不存在桥的,因为无论删除环中的任意一条边,这些点还是互相联通的(因为是无向边)。但是如果一条无向边连接的两个点不在一个环里(重边不算),那么删除这条边就会导致这两个点不再联通。所以可以使用tarjan算法求环。基于dfs给各个点打上时间戳。deep[x]表示的是深搜到这x点的顺序,low[x]表示的是这一点所能回溯连接到所有点中最早的点的编号。fa[x]表示的是x点在dfs中的上一个点。而pre表示的是并查集的数组。这里我们使用并查集的方式来记录哪些点属于于同一个环。
lca在这里面的作用是一步一步跳,把所有的桥找出来并且消除。