题目:
题意:
一个无向图可以有重边,下面q个操作,每次在两个点间连接一条有向边,每次连接后整个无向图还剩下多少桥(注意是要考虑之前连了的边,每次回答是在上一次的基础之上)
题解:
早就听说有用tajan求lca的了,今天第一次见,效率还真是低
求出桥来之后缩点,缩点运用并查集实现,每一个点组用父亲节点代表
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#define N 200005
using namespace std;
int tot,nxt[N*2],point[N],v[N*2],num,dfn[N],low[N],stack[N],ss,nn;
int belong[N],f[N],father[N],ans;bool vis[N];
void cl()
{
tot=0;memset(point,0,sizeof(point));
memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low));
nn=0;ss=0;num=0;ans=0;
}
void addline(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
int find(int x)
{
if (f[x]!=x) f[x]=find(f[x]);
return f[x];
}
void tarjan(int x,int fa)
{
dfn[x]=low[x]=++nn;vis[x]=1;stack[++ss]=x;
bool fff=0;
for (int i=point[x];i;i=nxt[i])
{
if (v[i]==fa && !fff){fff=1; continue;}
if (!dfn[v[i]])
{
father[v[i]]=x;
tarjan(v[i],x);
low[x]=min(low[x],low[v[i]]);
if (low[v[i]]>dfn[x]) ans++;
else f[find(v[i])]=find(x);
}
else low[x]=min(low[x],dfn[v[i]]);
}
}
int lca(int x,int y)
{
if (dfn[x]<dfn[y]) swap(x,y);
while (dfn[x]>dfn[y])
{
int xx=find(x),ff=find(father[x]);
if (xx!=ff) ans--,f[xx]=ff;
x=father[x];
}
while (y!=x)
{
int xx=find(y),ff=find(father[y]);
if (xx!=ff) ans--,f[xx]=ff;
y=father[y];
}
return ans;
}
int main()
{
int cnt=0,n,m,i,q;
scanf("%d%d",&n,&m);
while (n && m)
{
cl();int x,y;
for (i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
addline(x,y);
}
for (i=1;i<=n;i++) f[i]=i;
tarjan(1,0);
scanf("%d",&q);
printf("Case %d:\n",++cnt);
for (i=1;i<=q;i++)
{
scanf("%d%d",&x,&y);
printf("%d\n",lca(x,y));
}
printf("\n");
scanf("%d%d",&n,&m);
}
}