点双(以HDU - 3686 Traffic Real Time Query System为例)

71 篇文章 0 订阅
54 篇文章 0 订阅

定义

一张连通图中,若任意两个点之间都有至少两条没有重复点的路径,则这是一个点双。

求法

和tarjan系列的算法相似,在建dfs树的同时,可以发现每个割点都会将这个图分为几个点双,且每个割点都属于多个点双,因此,我们可以在dfs的同时,将所经过的边全部存起来,每当扫到一个割点就统计这个割点所分出的点双。

代码

void dfs(int now)
{
//  if(xh>D) throw(0);xh++;
    int p,q,ch=0;
    dfn[now]=low[now]=++tt;
    for(p=first[now]; p!=-1; p=bn[p].next)
    {
        q=p/2;
        if(use[q] || dfn[bn[p].to]>dfn[now]) continue;
        use[q]=1;
        sta[++st]=q;
        if(dfn[bn[p].to])
        {
            low[now]=min(low[now],dfn[bn[p].to]);
        }
        else
        {
            ch++;
            dfs(bn[p].to);
            low[now]=min(low[now],low[bn[p].to]);
            if(low[bn[p].to]>=dfn[now])
            {
                gd[now]=1;
                ss[q]=++cnt;
                for(;sta[st]!=q;st--)
                {
                    ss[sta[st]]=cnt;
                    //此时栈中pop掉的边连的两端即为该点双所包含的点。
                }
                st--;
            }
        }
    }
    if(now==gen&&ch<2) gd[now]=0;
}

例题(HDU - 3686 Traffic Real Time Query System)

题意

给出一幅图,询问从其中一边到另外一边必须要经过的点有几个。

做法

不难发现,就是询问要经过几个割点。
因此可以将所有点双缩为一个点,通过割点将它们连接,形成一棵“点双—割点—点双……”的树,然后求其中两条边所在点双之间的路径上有几个割点即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 10010
#define M 100100
#define M2 200100
#define D 10000000
using namespace std;

int n,m,bb,bb2,Q,tt,gj,gen,dfn[N],low[N],first[N],f2[N],cnt,fa[N],deep[N],top[N],so[N],size[N],dz[N],fz[N],st,ss[M],tmp[N],sta[M],xh;
bool gd[N],use[M],vis[N];
struct Bn
{
    int to,next;
} bn[M2],b2[M2];

inline void add(int u,int v)
{
    bb++;
    bn[bb].to=v;
    bn[bb].next=first[u];
    first[u]=bb;
}

inline void ad2(int u,int v)
{
    bb2++;
    b2[bb2].to=v;
    b2[bb2].next=f2[u];
    f2[u]=bb2;
}

void dfs(int now)
{
    int p,q,ch=0;
    dfn[now]=low[now]=++tt;
    for(p=first[now]; p!=-1; p=bn[p].next)
    {
        q=p/2;
        if(use[q] || dfn[bn[p].to]>dfn[now]) continue;
        use[q]=1;
        sta[++st]=q;
        if(dfn[bn[p].to])
        {
            low[now]=min(low[now],dfn[bn[p].to]);
        }
        else
        {
            ch++;
            dfs(bn[p].to);
            low[now]=min(low[now],low[bn[p].to]);
            if(low[bn[p].to]>=dfn[now])
            {
                gd[now]=1;
                ss[q]=++cnt;
                for(;sta[st]!=q;st--)
                {
                    ss[sta[st]]=cnt;
                }
                st--;
            }
        }
    }
    if(now==gen&&ch<2) gd[now]=0;
}

void Dfs(int now,int last)
{
    vis[now]=1;
    int i,j,t,mx=0,p,q;
    size[now]=1;
    for(p=f2[now]; p!=-1; p=b2[p].next)
    {
        t=b2[p].to;
        if(t==last || vis[t]) continue;
        deep[t]=deep[now]+(t>gj);
        dz[t]=dz[now]+1;
        fz[t]=now;
        Dfs(t,now);
        size[now]+=size[t];
        if(size[t]>mx)
        {
            mx=size[t];
            so[now]=t;
        }
    }
}

void D2(int now,int last)
{
    int i,j,t,p,q;
    if(!so[now]) return;
    else
    {
        top[so[now]]=top[now];
        D2(so[now],now);
    }
    for(p=f2[now]; p!=-1; p=b2[p].next)
    {
        t=b2[p].to;
        if(t==last || t==so[now]) continue;
        top[t]=t;
        D2(t,now);
    }
}

inline int lca(int u,int v)
{
    for(; top[u]!=top[v];)
    {
        if(dz[top[u]]<dz[top[v]]) swap(u,v);
        u=fz[top[u]];
    }
    return dz[u]<dz[v]?u:v;
}

int main()
{
    int i,j,p,q,l;
    while(~scanf("%d%d",&n,&m))
    {
        if(!n&&!m) return 0;
        tt=cnt=st=bb2=0,bb=1;
        memset(first,-1,sizeof(first));
        memset(f2,-1,sizeof(f2));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(so,0,sizeof(so));
        memset(use,0,sizeof(use));
        memset(vis,0,sizeof(vis));
        memset(gd,0,sizeof(gd));
        for(i=1; i<=m; i++)
        {
            scanf("%d%d",&p,&q);
            add(p,q),add(q,p);
        }
        for(i=1; i<=n; i++) if(!dfn[i]) gen=i,dfs(i);
        gj=cnt;
        for(i=1; i<=n; i++)
        {
            if(gd[i]) tmp[i]=++cnt;
        }
        for(i=1;i<=m;i++)
        {
            p=(i << 1),q=(i << 1)+1;
            if(gd[bn[p].to]) ad2(tmp[bn[p].to],ss[i]),ad2(ss[i],tmp[bn[p].to]);
            if(gd[bn[q].to]) ad2(tmp[bn[q].to],ss[i]),ad2(ss[i],tmp[bn[q].to]);
        }
        for(i=1; i<=cnt; i++)
        {
            if(vis[i]) continue;
            deep[i]=(i>gj);
            dz[i]=0;
            top[i]=i;
            Dfs(i,-1);
            D2(i,-1);
        }
        scanf("%d",&Q);
        for(i=1; i<=Q; i++)
        {
            scanf("%d%d",&p,&q);
            p=ss[p],q=ss[q];
            l=lca(p,q);
            printf("%d\n",deep[p]+deep[q]-2*deep[l]+(l>gj));
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值