割点

  研究了一晚上,终于把割点和强连通搞懂了……(感谢各位学长哈,问了你们那么多简单的东西而你们总是耐心地给我解答~)

  大体和Tarjan一样,就是low更新的时候和Tarjan绝对不一样!!!(搞懂这个费了我一个半小时……QAQ)

  在求强连通分量时,对于从u->v的边,如果v已经在栈中,那么说明u,v一定在同一个强连通分量中,所以到最后low[u]=low[v]是必然的,提前更新也不会有问题,但是在求割点时,low的定义有了小小的变化,不再是最早能追溯到的祖先,(因为是个无向图)没有意义,应该是最早能绕到的割点,为什么用绕到,是因为是无向边,所以有另一条路可以走,如果把dfn[v]改掉就会上翻过头,可能翻进另一个环中,所以wa掉。

  如果你看不懂,建议你画一个从上到下一次连接的三个点,每一个点都有一条到上一个点(它父亲的)反向边,除了最上面的那个,自己试试low和dfn两种更新,你就明白为什么在访问过这个点的情况下,需要用dfn来更新。

  另外,根节点要特别判断,所以当现在处理的这条边起点是根节点的时候,我们只是增加其出度,暂时不将它看作是一个割点。出来的时候特别判断一下就可以了。

  另外,数组一定要开够,一定要开够,一定要开够!就是因为这个我又没一次A!重要的事说三遍!

  下面上代码~

#include<cstdio>
#include<iostream>
using namespace std; 
#define maxn 1000005
int head[maxn],to[maxn],nxt[maxn],dfn[maxn],low[maxn];
int n,m,cnt,ans,num;
bool cut[maxn];
void add(int a,int b)
{
    to[++cnt]=b;
    nxt[cnt]=head[a];
    head[a]=cnt;
}
void Tarjan(int u,int fa)
{
    int am=0;
    dfn[u]=low[u]=++num;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(!dfn[v]) 
        {
            Tarjan(v,fa);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]&&u!=fa) cut[u]=1;
            if(u==fa) am++;
        }
        else
            low[u]=min(low[u],dfn[v]);
    }
    if(u==fa&&am>=2) cut[fa]=1;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    for(int i=1;i<=n;i++)
    if(!dfn[i]) Tarjan(i,i);
    for(int i=1;i<=n;i++)
    if(cut[i]) ans++;
    printf("%d
",ans);
    for(int i=1;i<=n;i++)
    if(cut[i]) printf("%d ",i);
    return 0;
}

   另:如果是求割边的话dfn和low取等就不是一条割边了。

原文地址:https://www.cnblogs.com/popo-black-cat/p/10226777.html