初见 | 图论 | 割点
前言
今天洛谷智能推荐按照这两个月来的管理给我推了个板子,不过这是个图论的板子,于是我就觉得可以学一下。学完之后觉得可以整理一下,于是就有了这一篇~
顺便这这一篇也是改变一下我直接写文章乱换行的陋习。不过我好像一个句子都写得不长,所以看起来没有什么太大的改观?
割点
首先我们需要了解什么是割点。
所谓割点,就是去掉这个点就能使整个图不再连通,下面我将用图简单描述一下。
比如对于这个图,我们可以举几个栗子来说明哪些是割点而哪些不是。
比如 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
写完力!
参考资料
-
题解 P3388 【模板】割点(割顶) 作者:shenbear
-
割点(Tarjan算法) 作者:collectionne