zoukankan      html  css  js  c++  java
  • tarjan用法——割点

    今天洛谷疯狂给我推送tarjan的题(它好像发现了我最近学tarjan),我正好做一做试一试(顺便练一练快读和宏定义)。

    其实找割点的tarjan和算强连通分量的tarjan不一样,找割点的判定条件比较狗。

    首先选定一个根节点,从该根节点开始遍历整个图(使用DFS)。

    对于根节点,判断是不是割点很简单——计算其子树数量,如果有2棵即以上的子树,就是割点。因为如果去掉这个点,这两棵子树就不能互相到达。

    对于非根节点,判断是不是割点就有些麻烦了。我们维护两个数组dfn[]和low[],dfn[u]表示顶点u第几个被(首次)访问,low[u]表示顶点u及其子树中的点,通过非父子边(回边),能够回溯到的最早的点(dfn最小)的dfn值(但不能通过连接u与其父节点的边)。对于边(u, v),如果low[v]>=dfn[u],此时u就是割点。

    但这里也出现一个问题:怎么计算low[u]。

    假设当前顶点为u,则默认low[u]=dfn[u],即最早只能回溯到自身。

    有一条边(u, v),如果v未访问过,继续DFS,DFS完之后,low[u]=min(low[u], low[v]);

    如果v访问过(且u不是v的父亲),就不需要继续DFS了,一定有dfn[v]<dfn[u],low[u]=min(low[u], dfn[v])。

    洛谷模板代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define duke(i,a,n) for(int i = a;i <= n;i++)
    #define lv(i,a,n) for(int i = a;i >= n;i--)
    using namespace std;
    int dfn[200010],low[200010],lst[200010],len = 0;
    int ans = 0,top = 0,n,m,tot = 0,cut[200010];
    bool vis[200010];
    template <class T>
    void read(T &x)
    {
        char c;
        bool op = 0;
        while(c = getchar(),c < '0' || c > '9')
            if(c == '-') op = 1;
        x = c - '0';
        while(c = getchar(),c >= '0' && c <= '9')
        {
            x = x * 10 + c - '0';
        }
        if(op == 1)
            x = -x;
    }
    struct node{
        int l,r,nxt;
    }a[200010];
    void add(int x,int y)
    {
        a[++len].l = x;
        a[len].r = y;
        a[len].nxt = lst[x];
        lst[x] = len;
    }
    void tarjan(int x,int fa)
    {
        int child = 0;
        dfn[x] = low[x] = ++tot;
    //    stc[++top] = x;
    //    vis[x] = 1;
        for(int k = lst[x];k;k = a[k].nxt)
        {
            int y = a[k].r;
            if(!dfn[y])
            {
                tarjan(y,fa);
                low[x] = min(low[x],low[y]);
                if (low[y] >= dfn[x] && fa != x) cut[x]=true;
                if(x == fa)
                    child++;
            }
    //        else if(vis[y])
            {
                low[x] = min(low[x],dfn[y]);
            }
        }
        if(x == fa && child >= 2)
            cut[x] = true;
    }
    int main()
    {
        memset(cut,false,sizeof(cut));
        read(n); read(m);
        duke(i,1,m)
        {
            int x,y;
            read(x);read(y);
            add(x,y);
            add(y,x);
        }
        duke(i,1,n)
        {
            if(dfn[i] == 0)
            {
                tarjan(i,i);
            }
        }
        int num = 0;
        duke(i,1,n)
        {
            if(cut[i])
                num++;
        }
        printf("%d
    ",num);
        duke(i,1,n)
        {
            if(cut[i])
                printf("%d ",i);
        }
        return 0;
    }
    /*
    6 7
    1 2
    1 3
    1 4
    2 5
    3 5
    4 5
    5 6
    */
  • 相关阅读:
    java-se 选择和冒泡排序
    获得最大数
    打印正反星星 先正后反星星
    Django链接MySQL,数据库迁移
    ORM常用字段及查询
    Django的View(视图)
    Pycharm设置默认HTML模板
    Django简介
    Django
    如何使用Python输出一个[斐波那契数列]
  • 原文地址:https://www.cnblogs.com/DukeLv/p/9403857.html
Copyright © 2011-2022 走看看