補習Tarjan
割點
定義:若從圖中刪除節點 x 以及所有與 x 關聯的邊之後,圖將被分成兩個或兩個以上的不相連的子圖,那麼稱 x 爲圖的割點。
如何求割點:在深搜樹中,如果對於某個點u,與它相連的點v(v不是u的父親)。
那麼如果 low[v]>=dfn[u] , 那麼也就是以v爲根的深搜子樹中的點所連接的點沒有已經標記時間戳的。
也就是以v爲根的子樹是封閉的,那麼一旦去掉點u,這棵子樹中的點就稱爲了一個新的連通分量。
那麼點u就是割點了。
割點的判斷:
1)如果這個點時根節點,並且兒子>=2則這個點就是割點
2)對於點U存在子節點V,V可以訪問到U的父節點,那麼點U就不是割點,否則就是割點
模板:
const int MAXN = 1e5;
int head[MAXN], cnt, tot, dfn[MAXN], low[MAXN];
int root;
int n, m;
set<int> ans;
struct Edge{
int to, dis, next;
}edge[MAXN << 1];
void add_edge(int u, int v, int dis) {
edge[++cnt].to = v;
edge[cnt].dis = dis;
edge[cnt].next = head[u];
head[u] = cnt;
}
void init() {
cnt = 1;
tot = 0;
memset(head, 0, sizeof(head));
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
ans.clear();
}
void Tarjan(int x, int fa) {
low[x] = dfn[x] = ++tot;
int son = 0;
for (int i = head[x]; i; i = edge[i].next) {
int to = edge[i].to;
if(to == fa) continue; //因爲是無向圖所以把回邊給跳過了
if (!dfn[to]) {
Tarjan(to, x);
low[x] = min(low[x], low[to]);
son++;
if ( x == root && son > 1) { //如果這個點時根節點,並且兒子>=2則這個點就是割點
ans.insert(x);
} else if (x != root && dfn[x] <= low[to] ) { //對於點U存在子節點V,V可以訪問到U的父節點,那麼點U就不是割點,否則就是割點
ans.insert(x);
}
} else {
low[x] = min(low[x], dfn[to]);
}
}
}
add_edge(u, v, 0);
for (int i = 1; i <= n; ++i) {
if (!dfn[i]){
root = i;
Tarjan(i, i);
}
}
專題:
POJ1144 (模板題求割點)
橋
定義:若從圖中刪除邊 e 之後,圖將分裂成兩個不相連的子圖,那麼稱 e 爲圖的橋或割邊。
如何求橋:在一張無向圖中,判斷邊 e (其對應的兩個節點分別爲 u 與 v)是否爲橋,
需要其滿足如下條件即可:dfn[u] < low[v]
我們發現從v節點出發,在不經過(u, v)的前提下,不管走哪一條邊,
我們都無法抵達u節點,或者比u節點更早出現的節點,
此時我們發現v所在的子樹似乎形成了一個封閉圈,那麼(u, v)自然也就是橋了.
模板:
#include<set>
#define LL long long
#define P pair<int, int>
using namespace std;
const int MAXN = 1e5 + 10;
int head[MAXN], cnt, dfn[MAXN], low[MAXN], tot;
set<P> ans;
struct Edge{
int to, dis, next;
}edge[MAXN << 1];
void add_edge(int u, int v, int dis) {
edge[++cnt].to = v;
edge[cnt].dis = dis;
edge[cnt].next = head[u];
head[u] = cnt;
}
void Tarjan (int x, int fa) {
low[x] = dfn[x] = ++tot;
for(int i = head[x]; i; i = edge[i].next) {
int to = edge[i].to;
if(to == fa) continue; //因爲是無向圖所以把回邊給跳過了
if(!dfn[to]) { //沒搜過
Tarjan(to, x);
low[x] = min(low[x], low[to]);
if(dfn[x] < low[to]) { //這條連線是關鍵線
ans.insert(make_pair(min(x, to), max(x, to))); //小的在前
}
} else { //搜過了,改當前的最短時間戳的值
low[x] = min(low[x], dfn[to]);
}
}
}
void init() {
cnt = 1;
tot = 0;
memset(head, 0, sizeof(head));
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
ans.clear();
}
add_edge(u, v, 0);
for (int i = 0; i < n; ++i) {
if(!dfn[i])
Tarjan(i, i);
}
專題:
UVA-796 (橋的模板題)