【模板】强连通分量——Tarjan 割点

嗯》。。。前面说了Tarjan缩点
现在来tarjan割点

看洛谷试炼场提高组模板,啥都有2333

先说割点的定义
就是你把这个点和与这个点相连的边都咔嚓了之后
原来相互连接在一起的一堆点,就变成相互连接在一起的两堆点
专业点就是一个联通快变成了两个联通快

值得注意的是根节点不是割点,不然就会像我一样傻傻两天不知错在何处

嗯…同样是tarjan
因为我们会发现在一个环内而不跟环外任意一点相连接的点不是割点
但是神奇的是在一个环内又和环外面的某点相连的点竟然是割点
嗯。。。。总之呢,涉及到了环的问题,tarjan貌似就挺好用

同样的dfn和low,不过有一些东西有所改变
首先tarjan( int pos ,int start) 对,我们要时刻记录一下最开始的那个头头start,有很多讨厌的细节跟他有关系

接下来是(!dfn[pos])的情况下,我们对low的处理方式没什么改变
不过我们应该想到这个时候可能有割点的出现
如果dfn[pos]0的时候,if( low[to] >=dfn[pos] &&pos!=start)
这个时候pos就是割点。因为后面这个if表示的意思就是,我的顺着我的儿子往下走是不会走回去的,那么我就算在某一环里面,我也是和环外的点有联系的,那么我就一定是割点。(当然,特殊情况就是pos
start,因为你的start可能就是可怜的只在环里面和外界没有联系的那个23333)
但也正是我们对start出现错误的提防,我们需要考虑一下start的感受
如果start真的是割点呢?于是我们就想到,当且仅当这个start有两个或两个以上之前没有进行处理过的儿子,它才是割点。稍画一画就知道了…
所以我们遇到dfn[pos]0 的时候记tot++;最后在pos的tarjan结束的时候
if(pos
fa&&tot>=2) pos是割点

然后是我们对所有的儿子都有这样一个处理:
low [pos] = min( low[pos] , dfn[to] )
这个跟缩点是一样的,只不过没有是否入栈的那个限制罢了。

接下来是我写的代码(⊙o⊙)…

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#if 0
Writer: Goes && G.S.M.
Just a game
Enjoy it
#endif
inline int read(){
	char ch=getchar();int sign=0,sum=0;
	while((ch!='-')&&(ch<'0'||ch>'9'))
		ch=getchar();
	if(ch=='-')	sign=1,ch=getchar();
	while(ch<='9'&&ch>='0'){
		sum*=10;sum+=ch-'0';ch=getchar();
	}
	if(sign==1)	return -sum;
	else return sum;
}
const int N=1000000+10;
int n,m;
struct ss{
	int to,nex;
}edge[N];int head[N],ecnt;
void add(int x,int y)
{
	edge[++ecnt]=(ss){y,head[x]};
	edge[++ecnt]=(ss){x,head[y]};
	head[x]=ecnt-1,head[y]=ecnt;
}
int dfn[N],low[N],cnt;
bool cut[N];
void tarjan(int pos,int fa){
	int tot=0;
	dfn[pos]=low[pos]=++cnt;
	for(int i=head[pos];i;i=edge[i].nex){ 
		int v=edge[i].to;
		if(!dfn[v]){
			tarjan(v,fa);
			low[pos]=min(low[pos],low[v]);
			if(low[v]>=dfn[pos]&&pos!=fa)
				cut[pos]=true;
			if(pos==fa)	tot++;
		}
		low[pos]=min(low[pos],dfn[v]);
	} 
	if(pos==fa&&tot>=2) cut[pos]=true;
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<=m;i++)
		add(read(),read());
	
	for(int i=1;i<=n;i++)
	if(!dfn[i]) tarjan(i,i);
	
	int ans=0;
	for(int i=1;i<=n;i++)
	if(cut[i]) ans++;
	
	cout<<ans<<endl;
	for(int i=1;i<=n;i++)
	if(cut[i]) cout<<i<<" ";
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GoesM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值