题目链接
题目要我们求出每次操作后剩余桥的数量,可以想到首先将图中所有的割边统计并将所有e-DCC缩点得到一棵树。对于每次操作的u,v,考虑u,v是否属于同一e-DCC。显然属于的情况没有影响,而不属于则会使u,v所在e-DCC到lca的路径上的所有边为非割边。利用并查集的路径压缩可以跳过已经是非割边的树边,时间复杂度O(M+QlogN)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define _rep(i,a,b) for(int i=(a);i<=(b);i++)
const int N=1e5+10;
const int M=2e5+10;
int ca,n,m,q,head[N],tot,fa[N],low[N],dfn[N],num,cnt,pre[N];
struct Edge{
int v,nx;
}edge[M<<1];
inline void addedge(int u,int v)
{
edge[tot].v=v;
edge[tot].nx=head[u];
head[u]=tot++;
}
int findfa(int x){return fa[x]==x?x:fa[x]=findfa(fa[x]);}
bool merge(int x,int y)
{
int fx=findfa(x);
int fy=findfa(y);
if(fx==fy)return false;
fa[fy]=fx;return true;
}
void tarjan(int u,int f)
{
dfn[u]=low[u]=++num;
int flag=0;
for(int i=head[u];~i;i=edge[i].nx)
{
int v=edge[i].v;
if(v==f&&!flag){flag=1;continue;}
if(dfn[v]==-1)
{
pre[v]=u;
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u])cnt++;
else merge(u,v);
}
else low[u]=min(low[u],dfn[v]);
}
}
inline int lca(int u,int v)
{
if(findfa(u)==findfa(v))return cnt;
if(dfn[u]>dfn[v])swap(u,v);
while(dfn[u]<dfn[v])
{
if(merge(pre[v],v))cnt--;
v=pre[v];
}
while(u!=v)
{
if(merge(u,pre[u]))cnt--;
u=pre[u];
}
return cnt;
}
int main()
{
//freopen("in.txt","r",stdin);
int u,v;
while(scanf("%d%d",&n,&m)&&n&&m)
{
printf("Case %d:\n",++ca);
cnt=num=tot=0;
memset(head,-1,sizeof(head));
memset(dfn,-1,sizeof(dfn));
memset(low,-1,sizeof(low));
pre[1]=1;
_rep(i,1,n)fa[i]=i;
_rep(i,1,m)scanf("%d%d",&u,&v),addedge(u,v),addedge(v,u);
tarjan(1,1);
scanf("%d",&q);
_rep(i,1,q)
{
scanf("%d%d",&u,&v);
printf("%d\n",lca(u,v));
}
puts("");
}
return 0;
}