图论

连通性相关

SCC & 缩点

这个概念是在有向图里的。

void tarj(ll u) {
	dfn[u] = low[u] = ++ tim; 
	stk.push(u); instk[u] = true; 
	for (auto x : G[u]) {
		if (!dfn[x]) {
			tarj(x); 
			low[u] = min(low[u], low[x]); 
		}
		else if (instk[x]) 
			low[u] = min(low[u], dfn[x]); 
	}
	if (low[u] == dfn[u]) {
		++ tot; ll x; 
		do {
			x = stk.top(); stk.pop(); 
			scc[tot].pb(x); 
			bel[x] = tot; 
			instk[x] = false; 
		} while (x != u); 
	}
}

缩点就是把相同 scc 的点缩成一个,然后在新的 dag 上做事情。

割点 / 点双

无向图里定义。
割点:如果去掉某个点与其出边,这个图就不再连通,这个点就是割点。
点双:不存在割点的图即为点双连通图,一个图中的每个极大点双连通子图称即为一个点双。

求割点

void tarj(ll u, ll rt) {
	dfn[u] = low[u] = ++ tim; 
	ll son = 0; 
	for (auto x : G[u]) {
		if (!dfn[x]) {
			tarj(x, rt); 
			low[u] = min(low[u], low[x]); 
			++ son; 
			if (u != rt && low[x] >= dfn[u]) vis[u] = true; 
		}
		else
			low[u] = min(low[u], dfn[x]); 
	}
	if (u == rt && son > 1) 
		vis[u] = true; 
}

求点双

void tarj(ll u, ll rt) {
	dfn[u] = low[u] = ++ tim; stk.push(u); 
	if (u == rt && !G[u].size()) {
		bcc[++ nbcc].pb(u); 
		return ; 
	}
	for (auto v : G[u]) {
		if (!dfn[v]) {
			tarj(v, rt); 
			low[u] = min(low[u], low[v]); 
			if (low[v] >= dfn[u]) {
				++ nbcc; ll x; 
				do {
					x = stk.top(); stk.pop(); 
					bcc[nbcc].pb(x); 
				} while (x != v); 
				bcc[nbcc].pb(u); 
			}
		}
		else 
			low[u] = min(low[u], dfn[v]); 
	}
}

杂题

P4819

显然最坏情况下,我们需要得知每个人的身份。考虑我们第一次问的一个人,如果他汇报出了若干个平民,我们肯定会继续问这些平民直到汇报出杀手。考虑缩点,相同 scc 内的人看作一个。那么在这个 dag 上,对于那些入度为 \(0\) 的点我们显然都要问一次,记个数为 \(c\),答案不就是 \(1 - \frac{c}{n}\)
但是考虑排除法。如果存在某个 scc,它的大小为 \(1\) 且出边到达的点不依赖它(入度不为 \(1\)),这个 scc 内的点可以不问,用剩下 \(n-1\) 个点来反推出他即可。如果存在就 \(c \to c-1\) 即可。

P3225

那我菜了呗。
显然考虑求割点。把割点删掉之后图断成了若干连通块,对每个连通块分别考虑:

  • 连通块中没割点。说明该连通块孤立,需要建两个,随便选两个建即可。
  • 连通块中有 \(1\) 个割点。把割点删掉就不合法了,需要建 \(1\) 个。这是因为如果建的位置塌了,可以跑出去。
  • 连通块有 \(2\) 个割点。无需建立。可以任选一边跑掉,总能跑到情况 \(2\) 的连通块。

那么做完了。考虑怎么实现,每次选了一个不是割点的点开搜,遇到割点就统计,不访问即可。

P1407

很妙啊。给一对夫妻连边,男到女连边。给情人女到男连边。然后可以发现,换配偶的过程等价于走一个环。判断一对夫妻是否在同一 scc 即可。

posted @ 2026-09-22 20:59  RainyRadio  阅读(3)  评论(0)    收藏  举报