给出一个图,保证没有自环和重边,接下来有两种操作
1:u,v 在u,v之间加一条边
2:u,v 查询u,v路径的必经边的个数(就相当于桥)
给出图之后,先用Tarjan算法求出所有桥,还有Belong数组(标记每个点属于哪个强连通分量)
接下来用dfs重新建图,实际上建出来是一个树,保存每个点的父节点,以及在树中的深度
接下来对于2操作,就是寻找u,v所在的点(强连通分量)的最短路径上的边的个数,也就是用LCA算法求(最小公共祖先)
对于1操作,要寻找u,v所在的点(强连通分量)的最短路径上的所有点(分量),也用LCA算法
找出来之后继续把它们缩为一个点,因为u,v的连接是这些点形成了环,也就是强连通分量
但是要把这些分量中所有的点都标记为一个点很费时间,就用类似并查集的结构uf数组
uf[u]保存u点现在归属于哪一个分量(1操作时要把路径上的点的uf都等于深度最小的那个点)
当查询u点的信息时就视为查询uf[u]的信息
接下来还有深度数组deep的问题,缩点之后原来点的子节点深度都有改变,波及很广,修改很费时
就按照deep[uf[u]]代替之前的deep[u]处理,但是这样之后LCA又出现了问题,因为深度不连续了
需要再对LCA修改,使得在深度不连续情况下依然能找到最小公共祖先
详见代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
// Tarjan算法 复杂度O(N+M)
const int MAXN = 110010;//点数
const int MAXM = 300010;//边数
struct Edge
{
int from,to,next;
bool f;
}edge[MAXM];
int head[MAXN],tot;
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];//Belong数组的值是1~scc
int Index,top;
int scc;//强连通分量的个数
bool Instack[MAXN];
int uf[MAXN],f[MAXN],deep[MAXN];
bool vis[MAXN];
void addedge(int u,int v)
{
edge[tot].from=u;edge[tot].to = v;edge[tot].next = head[u];edge[tot].f=false;head[u] = tot++;
}
void Tarjan(int u,int pre)
{
int v;
Low[u] = DFN[u] = ++Index;
Stack[top++] = u;
Instack[u] = true;
for(int i = head[u];i != -1;i = edge[i].next)
{
v = edge[i].to;
if(v==pre) continue;
if( !DFN[v] )
{
Tarjan(v,u);
if( Low[u] > Low[v] )Low[u] = Low[v];
if(Low[v]>DFN[u]) edge[i].f=true;
}
else if(Instack[v] && Low[u] > DFN[v])
Low[u] = DFN[v];
}
if(Low[u] == DFN[u])
{
scc++;
do
{
v = Stack[--top];
Instack[v] = false;
Belong[v] = scc;
}while( v != u);
}
}
void dfs(int u,int d)
{
deep[u]=d;
vis[u]=true;
for(int i=head[u];~i;i=edge[i].next)
{
int v=edge[i].to;
if(vis[v]) continue;
f[v]=u;
dfs(v,d+1);
}
}
void solve(int N)
{
memset(DFN,0,sizeof(DFN));
memset(Instack,false,sizeof(Instack));
Index = scc = top = 0;
for(int i = 1;i <= N;i++)
if(!DFN[i]) Tarjan(i,i);
int t=tot;
tot=0;
memset(head,-1,sizeof(head));
for(int i=0;i<t;i++)
if(edge[i].f)
{
int u=Belong[edge[i].from],v=Belong[edge[i].to];
addedge(u,v);
addedge(v,u);
}
memset(vis,false,sizeof(vis));
dfs(1,0);
f[1]=1;
for(int i=1;i<=scc;i++)
uf[i]=i;
}
void init()
{
tot = 0;
memset(head,-1,sizeof(head));
}
int find(int u)
{
while(uf[u]!=u) return uf[u]=find(uf[u]);
return u;
}
int LCA(int u,int v)
{
u=find(u);
v=find(v);
int ans=0;
while(u!=v)
{
while(deep[u]>deep[v])
{
u=f[u];
u=find(u);
ans++;
}
while(deep[v]>deep[u])
{
v=f[v];
v=find(v);
ans++;
}
if(u==v) break;
if(deep[u]==deep[v])
{
u=f[u];
u=find(u);
ans++;
v=f[v];
v=find(v);
ans++;
}
}
return ans;
}
void uflca(int u,int v)
{
u=find(u);
v=find(v);
int nu=u,nv=v;
while(u!=v)
{
while(deep[u]>deep[v])
u=f[u],u=find(u);
while(deep[v]>deep[u])
v=f[v],v=find(v);
if(u==v) break;
if(deep[u]==deep[v])
{
u=f[u],u=find(u);
v=f[v],v=find(v);
}
}
int rt=u;
u=nu;v=nv;
while(deep[u]>deep[rt])
{
int t=f[u];
uf[u]=rt;
u=t;u=find(u);
}
while(deep[v]>deep[rt])
{
int t=f[v];
uf[v]=rt;
v=t;v=find(v);
}
}
int main()
{
int T,n,m,q,u,v,op,kase=0;
scanf("%d",&T);
while(T--)
{
init();
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
solve(n);
scanf("%d",&q);
printf("Case #%d:\n",++kase);
while(q--)
{
scanf("%d%d%d",&op,&u,&v);
u=Belong[u];
v=Belong[v];
if(op==1)
{
uflca(u,v);
}
else if(op==2)
{
printf("%d\n",LCA(u,v));
}
}
}
return 0;
}