强联通分量学习笔记

过于丢人,现在才写出来学习笔记(,这玩意挺简单的其实。

前置知识,递归。

不太想画图,那就简单画画。

这张图就是一个强联通分量,定义就是这样,一堆有向边(也可以无向边,那就是加两条有向边嘛233)

从任意的 x 点 可以走到 任意的 y 点 则 x,y 属于同一个强联通分量。

首先需要一个 \(dfn[]\) , \(low[]\) , 以及一个栈 , 还有染色标号的数组记为 \(col[]\)

dfn 越小,那么越先被遍历。
那么如果没有 \(dfn[v]\) 的说明没有遍历下去啊,就可以一直递归下去,直到没有找到没有被标记 \(dfn\) 的为止
同样,一路递归,一路拿 low 记录你能到的最小值[判断条件:未入栈,未被染色],最小值具有传递性,所以可以通过 \(low_u == dfn_v\) 来判断是否遍历完能到的。

P2341 【模板】强连通分量 / [HAOI2006]受欢迎的牛

#include<cstdio>

using namespace std ;

int n , m ;
const int N = 1e4 + 10 ;
const int M = 5e4 + 10 ;
struct node {
	int v ;
	int nxt ;
} e[M << 1] ;
int head[N] , cnt = 0 ;
inline void Add(int u , int v) {
	e[++ cnt].v = v ;
	e[cnt].nxt = head[u] ;
	head[u] = cnt ;
	return ;
}
int dfn[N] , low[N] , co[N] ;
int idx = 0 ;
int st[N] ;
int tot = 0 ;
int top = 0 ;
int size[N] ;
inline int min(int x , int y) {
	return x < y ? x : y ;
}
inline void Tarjan(int u) {
	dfn[u] = low[u] = ++ idx ;
	st[++ top] = u ;
	for(register int i = head[u] ; i ; i = e[i].nxt) {
		int v = e[i].v ;
		if(! dfn[v]) {
			Tarjan(v) ;
			low[u] = min(low[u] , low[v]) ;
		}
		else if (! co[v]) low[u] = min(low[u] , dfn[v]) ;
	}
	if(low[u] == dfn[u]) {
		co[u] = ++ tot ;
		++ size[tot] ;
		while(st[top] ^ u) {
			++ size[tot] ;
			co[st[top --]] = tot ;
		}
		top -- ;
	}
	return ;
}

int degree[N] ;
signed main() {
	scanf("%d %d " , & n , & m) ;
	for(register int i = 1 ; i <= m ; i ++) {
		int u , v ;
		scanf("%d %d" , & u , & v) ;
		Add(u , v) ;
	}
	for(register int i = 1 ; i <= n ; i ++) 
		if(! dfn[i]) Tarjan(i) ;
	for(register int i = 1 ; i <= n ; i ++)
		for(register int j = head[i] ; j ; j = e[j].nxt) {
			if(co[i] ^ co[e[j].v]) degree[co[i]] ++ ;
		}
	int ans = 0 ;
	cnt = 0 ;
	for(register int i = 1 ; i <= tot ; i ++)
		if(! degree[i]) ans = i , cnt ++ ;
	if(cnt == 1) return ! printf("%d\n" , size[ans]) ;
	else printf("%d\n" , 0) ;
	return 0 ;	
}

题目挺多的,找找就完事了。

posted @ 2020-01-07 18:05  _Isaunoya  阅读(173)  评论(0编辑  收藏  举报