图论tarjan

有向图的强连通分量

强连通分量:在有向图G中,如果两个顶点vi,vj间有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。
dfn在代表时间戳,而low代表该点可以到达的点的最小dfn值,当low等于dfn的时候,就说明找到了一个强连通分量

code

int n,m,idx,top,cnt;

int dfn[maxn];      //这个点是第几个被访问的
int low[maxn];      //这个点所能到的点的dfn的最小值
bool vis[maxn];     //标记一个点是否在栈中
int s[maxn];        //栈

vector<int> a[maxn];        //图

void tarjan(int u)
{
    dfn[u] = low[u] = ++idx;
    s[++top] = u;
    vis[u] = 1;
    for(auto v : a[u])
    {
        if(!dfn[v])         //如果这个点还没被访问过
        {
            tarjan(v);
            low[u] = min(low[u],low[v]);    //回溯
        }
        else if(vis[v])     //如果这个点在栈内
        {
            low[u] = min(low[u],dfn[v]);
        }
    }
    if(dfn[u] == low[u])        //找到一组强连通分量,而且这一组强连通分量就是从s[top]到s[top]==u;
    {
        cnt++;      //记录强连通分量的个数
        while (s[top] != u)
        {   
            vis[s[top]] = 0;
            top--;
        }
        vis[s[top]] = 0;
        top--;
    }
}

例题

https://www.luogu.com.cn/problem/P2863

无向图的割点和桥

无向连通图中,如果删除某点后,图变成不连通,则称该点为割点
无向连通图中,如果删除某边后,图变成不连通,则称该边为桥

割点

一个顶点u是割点,当且仅当
(1)u为树根,且u有多于一个子树(不然删除这个点,还是联通的)
(2)u不为树根,且存在儿子v,使得dfn[u] <= low[v]

vector<int> a[maxn];        //图

int dfn[maxn];
int low[maxn];
bool cut[maxn];            //是否是割点
int idx;

void tarjan(int u,int fa)        //fa记录最开始的树根
{
    dfn[u]=low[u]=++idx;
    int child = 0;                //根的儿子个数
    for(auto i : a[u])
    {
        if(!dfn[i])
        {
            tarjan(i,fa);
            low[u] = min(low[u],low[i]);
            if(low[i] >= dfn[u] && u != fa) cut[u] = 1;        //u是割点
            if(u == fa) child++;                               //求根节点的子树个数
        }
        low[u] = min(low[u],dfn[i]);
    }
    if(child >= 2 && u == fa) cut[u] = 1;        //u为树根且儿子个数大于等于2
}

一条边(u,v)是桥,当且仅当(u,v)为树枝边,且满足dfn(u) < low(v)(前提是其没有重边)

vector<int> a[maxn];

int dfn[maxn],low[maxn],fa[maxn],idx,ans;

void tarjan(int u,int pre)
{
    dfn[u] = low[u] = ++idx;
    fa[u] = pre;
    for(auto i : a[u])
    {
        if(!dfn[i])
        {
            tarjan(i,u);
            low[u] = min(low[u],low[i]);
            if (low[i] > dfn[u]) ans++;                  //这个是桥
        }
        else if(i != pre)
        {
            low[u] = min(low[u],dfn[i]);
        }
    }
}

参考博客

初探tarjan算法
https://blog.csdn.net/LanQiLi/article/details/85009526

原文地址:https://www.cnblogs.com/hezongdnf/p/12089953.html