poj3694 Network 边双连通分量

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_36797646/article/details/82708983

题解:

边双模板。
边双的求法比较简单,先求出所有割边,然后不走割边,dfs一下,就可以找出所有了。
做法显然是先对原图跑一遍边双,然后每次加一条边,若两点属于同一个边双连通分量,那么答案不会改变;否则这两个连通分量路径上的边全都变成非割边。这个当然可以可以用树链剖分,但是看了题解有更为简洁的做法:用并查集维护每个点往上的第一条割边,这样每条边只会被删一次,代码写起来也十分简单。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=100010;
const int Maxm=200010;
const int inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,m,q,Case=0;
struct Edge{int y,next;}e[Maxm<<1],E[Maxm<<1];
int last[Maxn],len;
void ins(int x,int y)
{
    int t=++len;
    e[t].y=y;e[t].next=last[x];last[x]=t;
}
int Last[Maxn],Len;
void Ins(int x,int y)
{
    int t=++Len;
    E[t].y=y;E[t].next=Last[x];Last[x]=t;
}
int dfn[Maxn],low[Maxn],id,sta[Maxn],top,cnt,bel[Maxn],rt[Maxn],fa[Maxn][17],dep[Maxn];
bool mark[Maxm<<1],vis[Maxn];
void Tarjan(int x,int p)
{
    low[x]=dfn[x]=++id;
    sta[++top]=x;
    for(int i=last[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(i==p||i==(p^1))continue;
        if(!dfn[y])
        {
            Tarjan(y,i);low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x])mark[i]=mark[i^1]=true;
        }
        else low[x]=min(low[x],dfn[y]);
    }
}
void dfs(int x)
{
    vis[x]=true;
    bel[x]=cnt;
    for(int i=last[x];i;i=e[i].next)
    {
        if(mark[i])continue;
        int y=e[i].y;
        if(vis[y])continue;
        dfs(y);
    }
}
void DFS(int x,int f)
{
    fa[x][0]=f;dep[x]=dep[f]+1;
    for(int i=1;(1<<i)<=dep[x];i++)fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int i=Last[x];i;i=E[i].next)
    {
        int y=E[i].y;
        if(y==f)continue;
        DFS(y,x);
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=16;i>=0;i--)
    if((1<<i)<=dep[x]-dep[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=16;i>=0;i--)
    if((1<<i)<=dep[x]&&fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
int findrt(int x)
{
    if(rt[x]==x)return x;
    rt[x]=findrt(rt[x]);
    return rt[x];
}
int main()
{
    while(1)
    {
        n=read(),m=read();
        if(!n&&!m)break;
        Case++;
        memset(last,0,sizeof(last));len=1;
        memset(Last,0,sizeof(Last));Len=1;
        int ans=0;
        memset(mark,false,sizeof(mark));
        memset(vis,false,sizeof(vis));
        for(int i=1;i<=m;i++)
        {
            int x=read(),y=read();
            ins(x,y),ins(y,x);
        }
        top=cnt=id=0;
        memset(dfn,0,sizeof(dfn));
        Tarjan(1,0);
        for(int i=1;i<=n;i++)
        if(!vis[i])cnt++,dfs(i);
        for(int i=2;i<=len;i+=2)
        {
            int x=e[i].y,y=e[i^1].y;
            if(bel[x]==bel[y])continue;
            Ins(bel[x],bel[y]),Ins(bel[y],bel[x]);
            ans++;
        }
        dep[0]=-1;DFS(1,0);
        for(int i=1;i<=cnt;i++)rt[i]=i;
        q=read();
        printf("Case %d:\n",Case);
        while(q--)
        {
            int x=read(),y=read();
            x=bel[x],y=bel[y];
            if(x==y){printf("%d\n",ans);continue;}
            int z=LCA(x,y);
            while(1)
            {
                int t=findrt(x);
                if(dep[t]<=dep[z])break;
                ans--;rt[x]=rt[rt[fa[t][0]]];x=fa[t][0];
            }
            while(1)
            {
                int t=findrt(y);
                if(dep[t]<=dep[z])break;
                ans--;rt[y]=rt[rt[fa[t][0]]];y=fa[t][0];
            }
            printf("%d\n",ans);
        }
        puts("");
    }
}

猜你喜欢

转载自blog.csdn.net/baidu_36797646/article/details/82708983
今日推荐