zoukankan      html  css  js  c++  java
  • $割点割顶tarjan$

    原题

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    inline LL read () {
        LL res = 0 ;
        int f (1) ;
        char ch = getchar ();
        while (!isdigit(ch)) {
            if (ch == '-') f = -1 ;
            ch = getchar();
        }
        while (isdigit(ch)) res = (res << 1) + (res << 3) + (ch ^ 48),ch = getchar();
        return res * f ;
    }
    const int N = 1000000+10;
    int a[N] , nxt [N] , head[N] , dfn[N] , low[N] , cnt , k ;
    bool cut[N] , bst[N] ;
    inline void Add (int x ,int y) {
        a[++k] = y ; nxt[k] = head[x]; head[x] = k ; return ;
    }
    inline void tarjan (int u,int mr) {
        int rc = 0;
        dfn[u] = low[u] = ++ cnt ;
        for ( register int p = head[u] ; p ; p = nxt[p] ) {
            int v = a[p] ;
            if (! dfn [v]) {
                tarjan ( v , mr ) ;
                low[u] = min ( low[u] , low[v] );
                if (low[v] >= dfn[u] and u != mr) cut[u]=true;
                if (u == mr) rc ++ ;
            }
            low[u] = min ( low[u] , dfn[v] ) ;
        }
        if (u == mr and rc >= 2) cut[mr] = true;
    }
    signed main() {
        int n = read() ;
        int m = read() ;
        int ans = 0 ;
        for ( register int i = 1 ;i <= m ; i ++) {
            int x = read() ;
            int y = read() ;
            Add (x,y) ; Add (y,x) ;
        }
        for ( register int i = 1 ;i <= n ; i ++)
        if ( ! dfn[i] ) tarjan ( i , i ) ;
        for ( register int i = 1 ;i <= n ; i ++)
        if ( cut[i] ) ans ++ ;
        cout << ans << endl ;
        for ( register int i = 1 ;i <= n ; i ++)
        if ( cut[i] ) cout << i << ' ' ;
        return 0 ;
    }

    首先tarjan求割点的重点就是dfn和low数组的理解。

    dfn[i]就是时间戳,即在什么时刻搜索到了点i,

    low[i]则是i点能回溯到的dfn最小的祖先,

    搜索的时候判断一下当对于点x存在儿子节点y,使得dfn[x]<=low[y]则x一定是割点。

    因为只要x的子节点不能回溯到x的上面,就是没有返祖边超过x点,那么割掉x就能造成不连通了

    好啦,基本算法介绍完,就要讲几个问题了。

    首先,为什么此处

    low[a]=min(low[a],dfn[p]);

    不能写作

    low[a]=min(low[a],low[p]);

    在我的理解,由于此处是一张无向图,我们有双向建了边,导致节点可以回溯到它的父节点;

    而如果从它的父节点或其父节点的另一棵子树上有向上很多的返祖边,

    这时把子节点的low值赋为父节点的low,就可能导致其low==其父节点low<其父节点dfn,

    从而使本该是割点的点被忽视了,答案就少了,所以就wa了。

    另外本题还有几个注意点:

    1. 给的图不一定是连通图,即求每个联通块的割顶

    2. 输出格式别看错了2333

    3. 链式前向星开边要2倍

    不存在十全十美的文章 如同不存在彻头彻尾的绝望
  • 相关阅读:
    使用 VisualVM 进行性能分析及调优
    数据库乱码,频繁中断
    mysql 查看数据库中所有表的记录数
    MySQL数据库设置远程访问权限方法小结
    详解使用DockerHub官方的mysql镜像生成容器
    Docker的安装和使用说明——Docker for Windows
    CentOS7安装Docker与使用篇
    正则提取字符串中的字符串
    java parse 带英文单词的日期字符串 转 date (转化新浪微博api返回的时间)
    mysql function 中使用游标
  • 原文地址:https://www.cnblogs.com/qf-breeze/p/10526844.html
Copyright © 2011-2022 走看看