---
# System prepended metadata

title: Connectively

---

# Connectively
# 無向圖連通性
## 邊、一些定義
**樹邊(tree edge)**：真正在樹上的邊，由父親連到小孩
**回邊(back edge)**：子孫連回祖先
**前向邊(front edge)**：連到子孫的邊(不是樹)邊
**交錯邊(cross edge)**：連到非直系子孫
**內節點**:非根節點與葉節點(也就是說他至少會連兩個邊!)

**橋**：拔掉此邊後連通塊加1
**割點/關節點**：拔點此點後連通塊加1
**邊雙連通分量**：在此連通塊內拔掉任何一條邊仍然是連通塊，即連通塊內無橋
* **注意!**
    以點為主體，可能有邊不在任何集合內
![](https://hackmd.io/_uploads/Hkv0wsWvn.png)


**點雙連通分量(BCC)**：在此連通塊內拔掉任何一條點仍然是連通塊，即連通塊內無割點
* **注意!**
    以點為主體，可能同一點在不同集合內
![](https://hackmd.io/_uploads/SkXguoZwh.png)

* 性質
1. u到v定存在兩條不重複路徑
2. 對於u, v, w，存在由u到v再到w的簡單路徑(由1可以推得)
3. 對於任兩條邊，在同一簡單環上，則若且唯若他們屬於同點雙連通分量
## Tarjan(塔樣?)

定義low函數為不經過**父節點**所可以抵達的最淺深度
* u還沒走過:
    u會是v的小孩
    從u點走，回傳low(u)看是否比目前小(可能透過回邊走到祖先)
* u走過
    (v, u)會是一條回邊，檢查depth(u)是否比low(v)小，是的話更新
    
* **找橋**：如果depth[v] = low[v]，則父邊就會是橋
    代表 v 在不依靠父邊的情況下永遠沒辦法走到它的祖先，此時v的父邊就會是橋
```cpp=
void dfs(int v, int p)
{
    visited[v] = 1;
    if (v != p) // not root 
        low[v] = depth[p]+1;
    
    for (int u: adj[v])
    {
        if (u == p) continue;
        if (visited[u])
        {
            if (depth[u] < low[v]) low[v] = depth[u];
        }
        else
        {
            dfs(u);
            if (low[u] < low[v]) low[v] = low[u];
        }
    }
    if (low[v] == depth[v])
    {
        // (p, v) = bridge if v is not a root vertex
    }
    
}
```
* **找割點/關節點**：
如果v的子孫們沒有除了透過v到達祖先的方法,v為關節點
**v為root時**，若child >= 2 ，root為關節點

question:為何不能改為判斷式為 low[u] >= dis[u]
A: 可能繞回來的點恰好是關節點，遇上這情況會誤判
```cpp=
void dfs(int v, int p)
{
    visited[v] = 1;
    if (v!=p) low[v] = depth[p]+1;
    int chd_cnt = 0;
    for (int u:adj[v])
    {
        if (u==p) continue;
        if (!visited[u])
        {
            chd_cnt++, dfs(u, v);
            low[v] = min(low[v], low[u]);
            if (low[u] >= dis[v] && p!=-1)
                //v is a non-root cut-vertex
        }
        else if (dfs[u] < dfs[v])
            low[v] = min(low[v], dis[u]); 
    }
    if (p==-1 && chd_cnt > 1) 
        //the root of the tree is a cut-vertex
}
    
```

* **找邊雙連通分量**
在遞迴的過程
```cpp=
void dfs(int v, int p)
{
    visited[v] = 1;
    if (v != p) // not root 
        low[v] = depth[p]+1;
    stack<int> stk;
    stk.push(v);
    for (int u: adj[v])
    {
        if (v==p) continue;
        if (!visited[u])
        {
            dfs(v, u);
            low[v] = min(low[v], low[u]);
            if (low[v] > dis[u])
            {
                bcc_cnt++;
                while(stk.top()!=p)
                    bcc[bcc_cnt].push_back(stk.top())
            }
        }
    }
}
```
* **找點雙連通分量**


## 例題
### Cycling city
給一張無向圖，是否有(a, b)可以有三種之間可以有三組路徑(點不重複、邊不重複)

# 有向圖連通性

## 強連通分量
在此連通塊中每一個點都可以到別的點，可以**縮點**之後變成**DAG(有向無環圖)!**

## Kosaraju

## 2-SAT問題
Q：為甚麼不是無向圖?
如果(a或b)的話，若a是True，但b可能是true或false，所以是有向!(若你知道一個是false，另一個一定是True!)

建立¬a -> b, ¬b -> a的邊
接著用Kosaraju做!