题目链接:https://vjudge.net/problem/POJ-3694
具体思路:首先可以通过缩点的方式将整个图变成一个树,并且树的每条边是桥,但是我们可以利用dfn数组将整个图变成树,这样就可以省去缩点的过程了,同时lca的作用。假设有如下情况。
f->a f->b,这是缩点之后的,如果在a,b之间加一条边的话,从a->a和b的最近公共祖先节点-> b 之间的桥都会去除,这个时候就需要用到lca了/
AC代码(折磨了我两天--):
#include<iostream>
#include<stack>
#include<queue>
#include<map>
#include<stdio.h>
#include<cstring>
#include<string>
#include<iomanip>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
# define ll long long
# define maxn 500000+1010
# define inf 0x3f3f3f3f
int head[maxn],dfn[maxn],low[maxn];
int judge[maxn];
int father[maxn];
int edge,num,n,m,ans;
struct node
{
int to;
int nex;
} q[maxn];
void init()
{
memset(judge,0,sizeof(judge));
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
for(int i=1; i<=n; i++)
{
father[i]=i;
}
edge=0;
num=0;
ans=0;
}
void addedge(int fr,int to)
{
q[edge].to=to;
q[edge].nex=head[fr];
head[fr]=edge++;
}
void tarjan(int u,int root)
{
low[u]=dfn[u]=++num;
dfn[u]=dfn[root]+1;//建造树的过程。
for(int i=head[u]; i!=-1; i=q[i].nex)
{
int temp=q[i].to;
if(temp==root)continue;//如果 1-> 2 这条边已经访问过的话,2->1 就没有必要访问了,如果在访问的话会出问题的。
if(dfn[temp]==0)
{
father[temp]=u;
tarjan(temp,u);
low[u]=min(low[u],low[temp]);
if(low[temp]>dfn[u])//判断桥的方法,注意比较的是前一个的时间戳
{
ans++;
judge[temp]=1;
}
}
else if(temp!=u)
{
low[u]=min(low[u],dfn[temp]);
}
}
}
void lca(int t1,int t2)
{
while(dfn[t1]<dfn[t2])
{
if(judge[t2])ans--,judge[t2]=0;
t2=father[t2];
}
while(dfn[t1]>dfn[t2])
{
if(judge[t1])
{
ans--;
judge[t1]=0;
}
t1=father[t1];
}
while(t1!=t2)
{
if(judge[t1])ans--;
if(judge[t2])ans--;
judge[t1]=0;
judge[t2]=0;
t1=father[t1];
t2=father[t2];
}
}
int main()
{
int Case=0;
while(~scanf("%d %d",&n,&m)&&(n+m))
{
init();
int t1,t2;
for(int i=1; i<=m; i++)
{
scanf("%d %d",&t1,&t2);
addedge(t1,t2);
addedge(t2,t1);
}
tarjan(1,0);
int t;
printf("Case %d:\n",++Case);
scanf("%d",&t);
while(t--)
{
scanf("%d %d",&t1,&t2);
lca(t1,t2);
printf("%d\n",ans);
}
printf("\n");
}
return 0;
}