zoukankan      html  css  js  c++  java
  • 割点

    P3388 【模板】割点(割顶)

    题目背景

    割点

    题目描述

    给出一个(n)个点,(m)条边的无向图,求图的割点。

    输入输出格式

    输入格式:

    第一行输入(n,m)

    下面(m)行每行输入(x,y)表示(x)(y)有一条边

    输出格式:

    第一行输出割点个数

    第二行按照节点编号从小到大输出节点,用空格隔开

    说明

    (n,m)均为100000

    (tarjan)图不一定联通!!!


    割点:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合。

    以上是广义一点的定义,对这个题,要求的为求解所有联通子块的割点,且这个割点集合仅有一个元素。

    首先我们明确割点的几个性质:(在一个连通图中)

    1. 若点(u)是一个割点,则至少存在一对点(v_1,v_2),使得(v_1,v_2)的任何一个路径通过点(u)
    2. 对于一颗树,若某个点的入度大于1,则这个点为割点。

    有了以上两个性质,我们可以借助(tarjan)求强连通分量的思想求解割点了。

    首先,我们同样定义(dfn)(low)数组,分别代表 遍历到某点的时间 和 某点不通过它父亲下能到达的最小时间

    现在开始利用性质1,对于图中某父节点(u)(u)不为遍历开始的点)和其子节点(v),在刚进入子节点时,父亲的(dfn)是一定大于子节点的,我们遍历子节点不通过(u)能够到达的边,并用最小值更新(low)数组。遍历结束后,若(low[v]>=dfn[u])即点(v)不通过连接它父亲的边是不能到达它父亲上面的点的(即时间戳小于(dfn[u])的点),那么点(u)即满足了性质1,成为了割点。

    对于父节点(u)为遍历使点的时候,我们将连通图(G)看做是一颗树(当存在(u)为某环上的点时,等效为断开与(u)相连的某一条边,因为我们此时本来就是在探讨(u)是否为割点,所以断开其实不影响判断),此时,若这个(u)的入(出)度大于1,则(u)为割点。(其实这个模拟一下也是可以想明白的)

    最后说一个问题:在某点更新(low)时,若用来更新的点不能进去(即已经进去),一定要写成(low[now]=min(low[now],dfn[v])),而不是(low[now]=min(low[now],low[v]);)
    为什么?考虑这样一种情况,若点(i)恰好可以到达自己的父亲(u)(不通过父亲找到它的那条边),按照性质1,它父亲(u)仍然是割点。但是,如何它父亲(u)先经过某条边把自己的(low)更新小了,岂不是就错了吗,可以看看这个例子模拟理解一下。

    按1,2,3,3,4,5顺序遍历即可


    code:

    #include <cstdio>
    int min(int x,int y){return x<y?x:y;}
    const int N=100010;
    struct Edge
    {
        int to,next;
    }edge[N<<1];
    int head[N],cnt=0,n,m;
    void add(int u,int v)
    {
        edge[++cnt].next=head[u];edge[cnt].to=v;head[u]=cnt;
    }
    int time=0,dfn[N],low[N],ans[N];
    void tarjan(int now,int fa)
    {
        int child=0;
        dfn[now]=low[now]=++time;
        for(int i=head[now];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(!dfn[v])
            {
                tarjan(v,fa);
                if(low[v]>=dfn[now]&&now!=fa)
                    ans[now]=1;
                if(now==fa)
                    child++;
                low[now]=min(low[now],low[v]);
            }
            low[now]=min(low[now],dfn[v]);
        }
        if(now==fa&&child>1) ans[now]=1;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        int u,v;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v),add(v,u);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])
                tarjan(i,i);
        int cntt=0;
        for(int i=1;i<=n;i++)
            if(ans[i]) cntt++;
        printf("%d
    ",cntt);
        for(int i=1;i<=n;i++)
            if(ans[i]) printf("%d ",i);
        return 0;
    }
    
    

    2018.6.7

  • 相关阅读:
    Ubuntu-18.04设置花生壳内网穿透
    Ubuntu开启SSH
    Ubuntu查看版本信息
    Linux之使用mount挂载ISO镜像
    druid参数配置说明
    [转]Java多线程学习(吐血超详细总结)
    eclipse 更改官方配色
    Spring AOP拦截对Controller的请求时的配置失败
    Spring Security3详细配置
    “java.lang.IllegalArgumentException: Failed to evaluate expression ‘ROLE_USER’”报错的解决
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9150805.html
Copyright © 2011-2022 走看看