首页 > 其他 > 详细

无向图必经点、必经边的相关问题

时间:2019-07-30 23:52:09      阅读:139      评论:0      收藏:0      [点我收藏+]

无向图必经点、必经边的相关问题

一、 任意两点间路径的必经边

模板

首先考虑到必经边一定是原图的一条割边。

那么对于一个\(e-DCC\)中的点是不存在必经边的。不懂\(e-DCC\)相关内容?戳Here

那么很容易想到对于每一个\(e-DCC\)缩点,得到一棵树。两点间路径的必经边条数就是两点所在的

\(e-DCC\)对应的新节点的树上路径长度。直接树上倍增即可。

#include<bits/stdc++.h>
#define LL long long
#define DB double
#define RG register
#define IL inline
#define pb push_back
using namespace std;

const int N=2e5+3;

int n,m,Q,tot,cnt,num,Time,dfn[N],low[N];
int bri[N<<1],vis[N],bel[N],pos[N],dep[N],head[N],f[N][21];

struct edge{int x,y;}a[N];
struct EDGE{int next,to;}e[N<<1];

IL int gi() {
    RG int x=0,p=1; RG char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') p=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
    return x*p;
}

IL void New() {
    tot=0;
    memset(&e,0,sizeof(e));
    memset(head,0,sizeof(head));
    memset(vis,0,sizeof(vis));
}

IL void make(int x,int y) {
    e[++tot]=(EDGE){head[x],y},head[x]=tot;
    e[++tot]=(EDGE){head[y],x},head[y]=tot;
}

void Tarjan(int x,int fx) {
    RG int i,y;
    dfn[x]=low[x]=++Time;
    for(i=head[x];i;i=e[i].next)
        if(!dfn[y=e[i].to]) {
            Tarjan(y,x),low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x]) bri[i]=bri[i^1]=1;
        }
        else if(y!=fx) low[x]=min(low[x],dfn[y]);
}

void dfs(int x) {
    RG int i,y;
    vis[x]=1,bel[x]=cnt;
    for(i=head[x];i;i=e[i].next)
        if(!vis[y=e[i].to]&&!bri[i]) dfs(y);
}

void dfs2(int x) {
    RG int i,y;
    pos[x]=num,vis[x]=1;
    for(i=1;i<=20;++i) f[x][i]=f[f[x][i-1]][i-1];
    for(i=head[x];i;i=e[i].next)
        if(!vis[y=e[i].to])
            dep[y]=dep[x]+1,f[y][0]=x,dfs2(y);
}

IL int lca(int x,int y) {
    RG int i; 
    if(dep[x]<dep[y]) swap(x,y);
    for(i=20;i>=0;--i)
        if(dep[f[x][i]]>=dep[y]) x=f[x][i];
    if(x==y) return x;
    for(i=20;i>=0;--i)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}

int main()
{
    RG int i,x,y;
    n=gi(),m=gi(),tot=1;
    for(i=1;i<=m;++i)
        a[i].x=gi(),a[i].y=gi(),make(a[i].x,a[i].y);
    for(i=1;i<=n;++i)
        if(!dfn[i]) Tarjan(i,0);
    for(i=1;i<=n;++i)
        if(!vis[i]) ++cnt,dfs(i);
    for(i=1,New();i<=m;++i) {
        x=a[i].x,y=a[i].y;
        if(bel[x]!=bel[y]) make(bel[x],bel[y]);
    }
    for(i=1;i<=cnt;++i)
        if(!vis[i]) f[i][0]=0,dep[i]=1,dfs2(i);
    for(Q=gi();Q;--Q) {
        x=bel[gi()],y=bel[gi()];
        if(pos[x]!=pos[y]) puts("0"); // 是否在同一连通块
        else {
            if(x==y) puts("0"); // 是否在同一e-DCC
            else printf("%d\n",dep[x]+dep[y]-2*dep[lca(x,y)]);
        }
    }
    return 0;
}

二、 任意两点间路径的必经点

同上面一样,必经点一定是割点。位于同一个\(v-DCC\)中的两点间路径不存在必经点。

同样的进行\(v-DCC\)缩点。这时\(v-DCC\)缩点方式的优越体现了出来:

假设每一个\(v-DCC\)缩完的新点叫圆点,每一个割点对应的新点叫方点,

那么两点间路径的必经点个数就是 两点所对应的树上新点 间路径上的方点个数!

事实上,这也正体现了\(v-DCC\)缩点后原来的点的性质在新图中的分布特点:

割点的性质体现在割点对应的新点(方点)上;

非割点的性质体现在其所在\(v-DCC\)对应的新点(圆点)上。

void tarjan(int x) {
    RG int i,k,y,num=0;
    dfn[x]=low[x]=++Time,sta[++top]=x;
    if(x==RT&&head[x]==0) {
        vec[++cnt].push_back(x);
        return;
    }   // 孤立点的情况
    for(i=head[x];i;i=e[i].next)
        if(!dfn[y=e[i].to]) {
            tarjan(y),low[x]=min(low[x],low[y]);
            if(dfn[x]<=low[y]) {
                if(x!=RT||++num>1) cut[x]=1;
                vec[++cnt].push_back(x);
                do{
                    k=sta[top--]; 
                    vec[cnt].push_back(k);
                }while(k!=y);
            }
        }
        else low[x]=min(low[x],dfn[y]);
}

void dfs(int x,int fx) {
    RG int i,y;
    dep[x]=dep[fx]+1,f[x][0]=fx,g[x][0]=mtx[fx],vis[x]=1;
    // g[][]记录的是往上跳的方点个数
    for(i=1;i<=20;++i)
        f[x][i]=f[f[x][i-1]][i-1],g[x][i]=g[x][i-1]+g[f[x][i-1]][i-1];      
    for(i=head[x];i;i=e[i].next)
        if((y=e[i].to)!=fx) dfs(y,x);
}

IL int lca(int x,int y) {
    RG int i,dis=0;
    if(dep[x]<dep[y]) swap(x,y);
    for(i=20;i>=0;--i)
        if(dep[f[x][i]]>=dep[y]) dis+=g[x][i],x=f[x][i];
    if(x==y) return dis-mtx[y];
    for(i=20;i>=0;--i)
        if(f[x][i]!=f[y][i]) dis+=g[x][i]+g[y][i],x=f[x][i],y=f[y][i];
    return dis+g[x][0]+g[y][0]-mtx[f[x][0]];
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF&&n&&m) {
        New_case();
        RG int i,j,x,y;
        for(i=1;i<=m;++i)
            scanf("%d%d",&s[i].x,&s[i].y),make(s[i].x,s[i].y);
        for(i=1;i<=n;++i)
            if(!dfn[i]) RT=i,tarjan(i);
        for(i=1;i<=n;++i)
            if(cut[i]) bel[i]=++cnt,mtx[cnt]=1;
        //mtx[]记录的是缩点后树上的点是不是方点。
        for(i=1,New();i<=cnt;++i)
            for(j=0;j<vec[i].size();++j)
                if(cut[x=vec[i][j]]) make(i,bel[x]);
                else bel[x]=i;
        for(i=1;i<=cnt;++i)
            if(!vis[i]) dfs(i,0);
        for(scanf("%d",&Q);Q;--Q) {
            scanf("%d%d",&x,&y);
            if(bel[x]==bel[y]) puts("0");
            else printf("%d\n",lca(bel[x],bel[y]));
        }
    }
    return 0;
}
// 无向图两点间路径的必经点

上面两个问题事实上都涉及到点的性质在双连通分量中的体现,而下面问题则涉及到边的性质在双连通分量中的体现。

三、 任意两边间路径的必经点

模板

简单来讲,问题需要解决的只有边的归属问题(边丢在哪一个新点中),然后用同上的方法即可解决。

这个时候分类讨论一下:

1、边两端点都是非割点。那么这两点必然在一个\(v-DCC\)中,该边就对应该\(v-DCC\)对应的节点(圆点)。

2、边两端点有且仅有一个割点。这样的边对应 其非割点端点所在\(v-DCC\)对应的节点(圆点)。

3、边两端点都是割点。这样的边就对应 仅包含这两个割点的\(v-DCC\)对应的节点(圆点)。

? 这个具体讲一下,因为两点都为割点,那么先遍历到二者之一后,必然会把另外一个点和该点加入一个新的\(v-DCC\)

? 而且该\(v-DCC\)也仅有这两个点。因此我们可以在划分\(v-DCC\)的过程中,记录一下点被划分进入\(v-DCC\)编号

? (不包括最后单独放入的那个节点),然后对于这两割点,比较一下谁先被划分,就丢入它对应的\(v-DCC\)即可。

会抓紧补上一张图的。。。

Tarjan 中的记录 :

if(dfn[x]<=low[y]) {
    if(x!=RT||++num>1) cut[x]=1;
        vec[++cnt].push_back(x); // x不考虑
        do{
            k=sta[top--]; 
            vec[cnt].push_back(k);
            in[k]=cnt; // 记录一下编号
        }while(k!=y);
}

边的归属划分:

for(i=1;i<=m;++i) {
    x=s[i].x,y=s[i].y;
    if(!cut[x]) pos[i]=bel[x];
    else if(!cut[y]) pos[i]=bel[y];
    else {
        if(!in[x]) pos[i]=in[y];
        else if(!in[y]) pos[i]=in[x];
        else pos[i]=min(in[x],in[y]);
    }
}

然后同样考虑树上路径的方点个数即可。

四、 任意两边间路径的必经边

这个的话,边的划分没那么多复杂。

对于非割边,直接丢在其任一端点对应的\(e-DCC\)中(两端点在同一\(e-DCC\))。

对于割边,新建一个节点,其类似于上面的割点对应的新节点,也就是方点。

然后考虑树上路径的方点个数就好了!

五、声明:

这一类问题大概讲到这里为止了,如果有什么问题欢迎指出。

另外,圆点方点跟那个SMG圆方树没啥关系。。。hhh

无向图必经点、必经边的相关问题

原文:https://www.cnblogs.com/Bhllx/p/11273294.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!