【题解】poj3694 边双联通分量缩点+lca+并查集

题目链接
题目要我们求出每次操作后剩余桥的数量,可以想到首先将图中所有的割边统计并将所有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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值