[POJ3694]Network(桥+并查集)

题目:

我是超链接

题意:

一个无向图可以有重边,下面q个操作,每次在两个点间连接一条有向边,每次连接后整个无向图还剩下多少桥(注意是要考虑之前连了的边,每次回答是在上一次的基础之上)

题解:

早就听说有用tajan求lca的了,今天第一次见,效率还真是低
求出桥来之后缩点,缩点运用并查集实现,每一个点组用父亲节点代表

代码:

#include 
#include 
#include 
#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]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);
    }
}

你可能感兴趣的:(图论,并查集)