$割點割頂tarjan$

原題c++

#include <bits/stdc++.h>
using namespace std; typedef long long LL; inline LL read () { LL res = 0 ; int f (1) ; char ch = getchar (); while (!isdigit(ch)) { if (ch == '-') f = -1 ; ch = getchar(); } while (isdigit(ch)) res = (res << 1) + (res << 3) + (ch ^ 48),ch = getchar(); return res * f ; } const int N = 1000000+10; int a[N] , nxt [N] , head[N] , dfn[N] , low[N] , cnt , k ; bool cut[N] , bst[N] ; inline void Add (int x ,int y) { a[++k] = y ; nxt[k] = head[x]; head[x] = k ; return ; } inline void tarjan (int u,int mr) { int rc = 0; dfn[u] = low[u] = ++ cnt ; for ( register int p = head[u] ; p ; p = nxt[p] ) { int v = a[p] ; if (! dfn [v]) { tarjan ( v , mr ) ; low[u] = min ( low[u] , low[v] ); if (low[v] >= dfn[u] and u != mr) cut[u]=true; if (u == mr) rc ++ ; } low[u] = min ( low[u] , dfn[v] ) ; } if (u == mr and rc >= 2) cut[mr] = true; } signed main() { int n = read() ; int m = read() ; int ans = 0 ; for ( register int i = 1 ;i <= m ; i ++) { int x = read() ; int y = read() ; Add (x,y) ; Add (y,x) ; } for ( register int i = 1 ;i <= n ; i ++) if ( ! dfn[i] ) tarjan ( i , i ) ; for ( register int i = 1 ;i <= n ; i ++) if ( cut[i] ) ans ++ ; cout << ans << endl ; for ( register int i = 1 ;i <= n ; i ++) if ( cut[i] ) cout << i << ' ' ; return 0 ; }

 

首先tarjan求割點的重點就是dfn和low數組的理解。git

dfn[i]就是時間戳,即在什麼時刻搜索到了點i,算法

low[i]則是i點能回溯到的dfn最小的祖先,數組

搜索的時候判斷一下當對於點x存在兒子節點y,使得dfn[x]<=low[y]則x必定是割點。spa

由於只要x的子節點不能回溯到x的上面,就是沒有返祖邊超過x點,那麼割掉x就能形成不連通了code

好啦,基本算法介紹完,就要講幾個問題了。blog

首先,爲何此處get

low[a]=min(low[a],dfn[p]);it

不能寫做class

low[a]=min(low[a],low[p]);

在個人理解,因爲此處是一張無向圖,咱們有雙向建了邊,致使節點能夠回溯到它的父節點;

而若是從它的父節點或其父節點的另外一棵子樹上有向上不少的返祖邊,

這時把子節點的low值賦爲父節點的low,就可能致使其low==其父節點low<其父節點dfn,

從而使本該是割點的點被忽視了,答案就少了,因此就wa了。

另外本題還有幾個注意點:

  1. 給的圖不必定是連通圖,即求每一個聯通塊的割頂

  2. 輸出格式別看錯了2333

  3. 鏈式前向星開邊要2倍

相關文章
相關標籤/搜索