初见 | 图论 | 割点

前言

今天洛谷智能推荐按照这两个月来的管理给我推了个板子,不过这是个图论的板子,于是我就觉得可以学一下。学完之后觉得可以整理一下,于是就有了这一篇~

顺便这这一篇也是改变一下我直接写文章乱换行的陋习。不过我好像一个句子都写得不长,所以看起来没有什么太大的改观?

割点

首先我们需要了解什么是割点。

所谓割点,就是去掉这个点就能使整个图不再连通,下面我将用图简单描述一下。

比如对于这个图,我们可以举几个栗子来说明哪些是割点而哪些不是。

比如 1 就是一个割点,因为去掉它之后原来的图被分为了 “6,7” 和 “2,3,4,5” 两个部分,如下图。

比如 3 就不是一个割点,因为去掉它之后原图还是连通的,并没有移动,如下图。

这两个栗子就很好的说明了什么是割点了吧~

求割点

直接暴力 DFS

首先说明,这个肯定方法牙白,因为想想也知道这个复杂度上天,而且细节也不少。

由于我也没用过这种方法,所以就不说了,想试试的同学可以写写,反正我是懒得去弄。

Tarjan

哦怎么又是 Tarjan

这里的 Tarjan 算法和算强连通分量那里的 Tarjan 据说很像,但是我没做过于是就不太清楚。这同时也反应了我学 OI 时各个知识点之间极其不平衡,甚至直接就是没顺序随便学的。

思路简述

我这里就直接结合着程序来讲讲吧,因为本来分离程序写了写,但是写出来没内味。

这里结合的题目是[洛谷P3388]

What we need ?

struct road
{
    LL nex,to;
}
r[MXX];
LL head[MXX],cnt,dfn[MXX],low[MXX],sum,ans,n,m,a,b;
bool is[MXX];//判断是不是割点,便于统计和输出

\(\text{head}\) 和那个结构体 \(\text{road}\) 就很显然是要链式前向星存图啦~

而后面这些 \(\text{dfn low}\) 则是 Tarjan 算法专门要的两个数组,前者 \(\text{dfn}[x]\) 代表的是在 DFS 时 \(x\) 被遍历到的顺序。还是以我们刚才那张图来举例:

我们从 1 开始遍历的话(当然 1 显然不是根),那么 \(\text{dfn}[1]=1\ ,\ \text{dfn}[6]=2\ , \ \text{dfn}[7]=3\)

接下来看 \(\text{low}[x]\) 则是 \(x\)不通过父节点能回溯到的 \(\text{low}\) 最小的祖先。而对于 \(\text{low}[x]\) 是它自己的点 \(x\),这个点就有可能是割点。而真正的判断方法是:

对于边 \((u,v)\)\(\text{low }[v]\ge \text{dfn}[u]\) 这个点就是割点。

接下来最大的问题就是如何去求每个点的 \(\text{low}\)

What we need to do ?

注意到我们题目中实际上是一个无向图,于是我们需要换一张图来看。

我们需要从 1 开始 DFS 整个图,记录 \(\text{dfn}\),最初 \(\text{dfn = low}\)

假如 DFS 到的是根节点,那么就记录一下它有几个子树,若大于等于 2 ,那他就是割点,因为显然去掉它整个图就不连通了。

否则我们就正常的 DFS,然后比较 \(\text{low}[x]\)\(\text{dfn}[x]\),若是前者大于等于后者,说明无论如何这个点都不能到达比它的父节点 dfn 更小的点,于是这个点就是割点。

然后就可上 Code 稍微理解一下~

Code

#include <bits/stdc++.h>
#define Heriko return
#define Deltana 0
#define S signed
#define LL long long
#define R register
#define I inline
#define CI const int
#define mst(a, b) memset(a, b, sizeof(a))
#define ON std::ios::sync_with_stdio(false)
using namespace std;
I void fr(LL & x)
{
    LL f=1;
    char c=getchar();
    x=0;
    while(c<'0' or c>'9')
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while (c>='0' and c<='9') 
    {
        x=(x<<3)+(x<<1)+c-'0';
        c=getchar();
    }
    x*=f;
}
I void fw(LL x)
{
    if(x<0) putchar('-'),x=-x;
    static LL stak[35];
    LL top=0;
    do
    {
        stak[top++]=x%10;
        x/=10;
    }
    while(x);
    while(top) putchar(stak[--top]+'0');
}
CI MXX=2e5+6;
struct road
{
    LL nex,to;
}
r[MXX];
LL head[MXX],cnt,dfn[MXX],low[MXX],sum,ans,n,m,a,b;
bool is[MXX];
I void add(LL x,LL y)
{
    r[++cnt].to=y;
    r[cnt].nex=head[x];
    head[x]=cnt;
}
void DFS(LL x,LL sx)
{
    LL son=0;dfn[x]=low[x]=++sum;
    for(R LL i=head[x];i;i=r[i].nex)
    {
        LL j=r[i].to;
        if(!dfn[j])
        {
            DFS(j,x);
            low[x]=min(low[x],low[j]);
            if(low[j]>=dfn[x] and x!=sx) is[x]=true;
            if(x==sx) ++son;
        }
        low[x]=min(low[x],dfn[j]);
    }
    if(son>1 and x==sx) is[x]=true;
}
S main()
{
    fr(n),fr(m);
    for(R LL i=1;i<=m;++i) fr(a),fr(b),add(a,b),add(b,a);
    for(R LL i=1;i<=n;++i) if(!dfn[i]) DFS(i,i);
    for(R LL i=1;i<=n;++i) if(is[i]) ++ans;
    fw(ans);putchar('\n');
    for(R LL i=1;i<=n;++i) if(is[i]) fw(i),putchar(' ');
    Heriko Deltana;
}

End

写完力!

参考资料

posted @ 2021-06-17 11:12  HerikoDeltana  阅读(218)  评论(0编辑  收藏  举报