建中校隊培訓
by 劉澈
OUTLINE
NOT COVERED
INTRO
ID: cheissmart2 cheissmart
MOTTO: 8wcp
8w
序
賴賴和Wiwi是一對夫妻
他們的兩個小孩: 小賴 小wi
今天考慮的圖都沒有自環
好題推薦: tioj 2213
序列是一種特別的樹
DEFINITION
題目如果是無根樹,就選隨便一個根,這個操作叫定根
定完根之後,就可以定義以每個點為根的子樹
1
2
5
3
4
1
1
6
1
7
1
8w
根
前綴和
序列前綴和其實是一種(退化的?)DP
還是你們都用FFT做的
前綴和對應到樹上是子樹和
int dp[N], a[N];
vector<int> G[N];
void dfs(int u, int p = -1) {
dp[u] = a[u];
for(int v:G[u]) if(v != p) {
dfs(v, u);
dp[u] += dp[v];
}
}
一些集的定義
樹的最小支配集:點集中取出儘量少的點,使剩下的點與取出來的點都有邊相連
樹的最小點覆蓋:點集中取出儘量少的點,使得所有邊都與選出的點相連
樹的最大獨立集:點集中取出儘量多的點,使得這些點兩兩之間沒有邊相連
註: 兩個做法都是 \[ O(N) \]
加強版
樹的最小權支配集
樹的最小權點覆蓋
樹的最大權獨立集
樹的最大權獨立集
另外兩個做法類似(left as an exercise for the reader)
Text
dp[u][0] := 考慮在 u 的子樹中,不選 u 的最大權獨立集
dp[u][1] := 考慮在 u 的子樹中,選 u 的最大權獨立集
int dp[N][2];
void dfs(int u, int p = -1) {
dp[u][1] = w[u]; // weight of u
for(int v:G[u]) if(v != p) {
dfs(v, u);
dp[u][0] += max(dp[v][0], dp[v][1]);
dp[u][1] += dp[v][0];
}
}
int max_weight_indep_set() {
dfs(1);
return max(dp[1][0], dp[1][1]);
}
換根DP
轉移也可以DP
有一個樹,賴賴要選擇一個獨立集,Wiwi 要從剩下來的點拔掉一些點,讓任意兩個賴賴選的點都不連通
賴賴要最大化拔掉點個數,Wiwi要最小化
請問拔掉點個數
\[ N\leq10^5\]
也可以想成是DP多加一維在滾動
EXERCISES
8w所在國家是一個樹,因為要舉行婚禮,所以要找一條簡單路徑讓花車走,一條道路如果洽有一個端點在路徑上,就必須封住,以擾亂社會為樂的8w想要最大化封住的道路數
\[N \leq 2\times 10^5\]
8w所在國家是一個樹,他們要舉要行婚禮,每一條街有兩種可能性(會出席婚禮 or 不會出席婚禮) ,8w每次可以選擇一條簡單路徑,然後改變(toggle)路徑上所有人的狀態,對於每條街,有三種可能性
請問8w最少操作次數
\[N \leq 10^5\]
EXERCISE
註: 有些題目可能需要會別的(更進階的)東西
Reference: https://codeforces.com/blog/entry/68138
Types of edges
Tree edge: 走到兒子的邊
Back edge: 走到祖先的邊
Forward edge: 走到非兒子的子孫的邊
Cross edge: 走到非直系血親的邊
1
2
5
3
4
1
1
6
1
7
1
8w
無向圖只有 tree edges 和 back edges!!
MOTIVATION
8w在一張連通無向圖上生活。有一天,賴賴在蛋糕上放"WIWI"字母蠟燭時,不小心放成"WWII",想到第二次世界大戰,賴賴就開始害怕: 如果有戰爭把8w家園的一條路炸毀,會不會讓家園不連通
請找出所有路(邊)使得如果該路被炸毀,圖就會不連通
\[ N,M \leq 10^6 \]
DEFINITION
對於一張無向連通圖,一條邊是橋(bridge) iff 把他拔掉會使圖不連通
DEFINITION
對於一張無向連通圖,如果沒有橋,則這張圖是邊雙連通(2-edge connected)的
Tarjan算法的前奏
Back edge不可能是bridge!!
只要檢查 tree edge
\[ \implies \]
Tarjan的想法
對於一條 tree edge 你會發現他是 bridge 的充分必要條件是:
下面那個點的子樹裡所有 back edge 的終點都在子樹裡
怎麼檢查呢?
怎麼檢查呢?
樹DP
DP什麼
low[u] := 起點在 u 的子樹中的 back edges 的終點當中, dfs 序最小值
註: dfs序改成深度也可以,但是 scc 就不能用深度但可以用dfs序,所以推薦就一律用dfs序
bool vis[N];
int tin[N], low[N], timer;
vi G[N];
void dfs(int u, int p = -1) {
vis[u] = true;
tin[u] = low[u] = timer++;
for (int v : G[u]) if(v != p) {
if (vis[v]) {
low[u] = min(low[u], tin[v]); // 有一條從 u 開始的 back edge
} else {
dfs(v, u);
low[u] = min(low[u], low[v]); // 轉移 dp
if (low[v] == tin[v])
IS_BRIDGE(u, v);
}
}
}
SOME PROPERTIES
G is a connect graph.
* 8w Property
An edge (u, v) in G is a bridge iff u, v are not connected after removing (u, v) from G.
PROOF: trivial
* 8wcp Corollary
For any simple cycle C in a graph G, each edge in C is not a bridge in G.
PROOF: trivial
* 8wcpOrz Property
If G' is a connected subgraph of G, e is a bridge in G and e is in G',
then e is also a bridge in G'.
PROOF: trivial
記清楚名字喔,我等等會直接用
註: 其實這裡G, G' 是不是連通不重要,但是我前面只有對連通圖定義什麼是橋,事實上橋可以更廣義的定義成,拔掉會使連通塊數增加,但我懶得改了
MOTIVATION
Q: Given a connected graph G, remove as few edges as possible so that every connected component is 2-edge connected.
By 8wcpOrz property, all bridges have to be removed.
Is that enough?
Answer: Yes!!
用8w, 8wcp, 和 8wcpOrz 就可以證出來了
邊雙連通分量(BCC)
因為把所有橋拔掉,剩下來每一個連通塊都是邊雙連通的,所以每一塊叫做一個邊雙連通分量
另一種定義
等價的定義: maximal 2-edge connected subgraph
假設 G 的一個子圖 G' 是邊雙連通的,顯然
所以 G' 一定要掉在把橋拔掉後的一個連通塊裡
\[ \implies \]
註: maximal 是指 G 不存在一個子圖嚴格包含G'
Tarjan:我又回來了
怎麼找到所有 BCC 呢?
一種顯然的作法是把 bridge 拔掉然後 DFS 找連通塊
但是其實可以找橋的時候就順便做完
stack!!
Tarjan
int tt, bcc_cnt, vis[N], low[N], bcc[N];
vi G[N], who[N], s;
void assign(int u, int bcc_id) {
bcc[u] = bcc_id;
who[bcc_id].PB(u);
}
void dfs(int u, int p = -1) {
low[u] = vis[u] = ++tt;
s.PB(u);
for(int v:G[u]) if(v != p) {
if(vis[v]) low[u] = min(low[u], vis[v]);
else {
dfs(v, u);
if(low[v] > vis[u]) {
++bcc_cnt;
int k;
do {
assign(k = s.back(), bcc_cnt);
s.pop_back();
} while(k != v);
}
low[u] = min(low[u], low[v]);
}
}
if(pa == -1) {
++bcc_cnt;
for(; s.size(); s.pop_back())
assign(s.back(), bcc_cnt);
}
}
Bridge Trees
一定會是樹!!
總結
對一張無向連通圖,可以用 Tarjan 算法做 BCC 分解,把每個 BCC 縮成一個點得到 bridge tree
\[O(N+M)\]
對於有些問題,於把圖的問題變成樹的問題會大大簡化問題
CF1220E Tourism
EXERCISES
跟邊的差不多
DEFINITION
對於一張無向連通圖,一個點是割點(articulation vertex) iff 把他拔掉會使圖不連通
DEFINITION
對於一張無向連通圖,如果沒有割點,則這張圖是點雙連通(biconnected)的
Tarjan的想法
對於一個不是根點 u 你會發現他是割點的充分必要條件是:
有一個小孩子樹裡所有 back edge 的終點都在子樹裡或是u
對於根點,他是割點的充分必要條件是: dfs tree 上小孩個數>=2
e.g. 賴賴跟Wiwi有兩個小孩(小賴跟小Wi)
DP什麼
low[u] := 起點在 u 的子樹中的 back edges 的終點當中, dfs 序最小值
註: dfs序改成深度也可以,但是 scc 就不能用深度但可以用dfs序,所以推薦就一律用dfs序
bool vis[N];
int tin[N], low[N], timer;
vi G[N];
void dfs(int u, int p = -1) {
vis[u] = true;
tin[u] = low[u] = timer++;
int child_cnt = 0;
for (int v : G[u]) if(v != p) {
if (vis[v]) {
low[u] = min(low[u], tin[v]); // 有一條從 u 開始的 back edge
} else {
dfs(v, u);
if(p != -1 && low[v] >= tin[u])
IS_CUTPOINT(u); // u separates v from p
child_cnt++, low[u] = min(low[u], low[v]); // 轉移 dp
}
}
if(p == -1 && child_cnt > 1) IS_CUTPOINT(u);
}
SOME LEMMAS
G is a connected graph.
* 8e7 loves wiwi Lemma
If A, B are biconnected subgraphs of G and |V(A) ∩ V(B)| >= 2,
then A U B is also a biconnected subgraph.
PROOF. 我懶得用打的,現場講XD
* wiwi loves 8e7 Corollary
If A, B are biconnected subgraphs of G and |E(A) ∩ E(B)| >= 1,
then A U B is also a biconnected subgraph.
* 8w 1314 Observation
If (u, v) is an edge of G,
then ({u, v}, {(u, v)}) is a biconnected subgraph of G.
* 8w forever Corollary
If e is an edge of G,
then e belongs to at least one biconnected subgraph of G.
SUMMARY
點雙連通分量(BCC)
Definition: A biconnected component is a maximal biconnected subgraph
剛剛邊雙連通分量是把點集partition
那現在也一樣嗎?
慘!!點有交集
那邊呢?
好像是 partition 耶!
PARTITION?
比較
邊雙連通分量是把點集partition
點雙連通分量是把邊集partition
By 「wiwi loves 8e7 Corollary」and 「8w forever Corollary」the set of all BCC partitions all edges.
怎麼找到所有 BCC 呢?
理論上對於每個 BCC 要把邊找出來,但實際上通常把每個 BCC 的點找出來比較有用(雖然同一個點可能在不只一個 BCC 裡)
Tarjan
int low[N], tin[N], id[N], t, cnt;
vi s, bcc[N], G[N];
void dfs(int u, int p = 0) {
low[u] = tin[u] = ++t;
s.PB(u);
for(int v:G[u]) if(v != p) {
if(tin[v]) low[u] = min(low[u], tin[v]);
else {
dfs(v, u);
if(low[v] >= tin[u]) {
cnt++;
int k;
do{
k = s.back(); s.pop_back();
bcc[k].PB(cnt);
} while(k != v);
bcc[u].PB(cnt);
}
low[u] = min(low[u], low[v]);
}
}
}
bool iscut[N];
vi T[N];
void build() {
dfs(1);
for(int i = 1; i <= n; i++) {
if(bcc[i].size() > 1) {
id[i] = ++cnt;
iscut[id[i]] = true;
for(int j:bcc[i]) {
T[id[i]].PB(j);
T[j].PB(id[i]);
}
} else {
id[i] = bcc[i][0];
}
}
}
找到BCC也可以順便找割點
Claim: A vertex is an articulation vertex iff is belongs to more that 1 BCC.
證明時間!!
BLOCK-CUT-TREE
對一張無向連通圖,可以用 Tarjan 算法做 BCC 分解,得到 block-cut tree
\[O(N+M)\]
對於有些問題,於把圖的問題變成樹的問題會大大簡化問題
總結
EXERCISES
CSES 1705
EAR DECOMPOSITION
\8w教我圖論/
講了這麼多無向圖,來看看有向圖
MOTIVATION
8w家庭在一張有向圖上生活,但是因為8e7都不買奶粉,所以小賴和小wi都要自己到處找食物,如果其中一個人找到食物,就會拿去找另一個人一起分享,請問是否對於任意兩個點a, b,都存在一條從 a 走道 b 的路徑
DEFINITION
A direct graph is said to be strongly connected if every vertex is reachable from every other vertex.
DEFINITION
First definition:
A strongly connected component of a directed graph G is a maximal subgraph that is strongly connected.
Second definition:
Define a relation x R y iff both vertices are reachable from the other vertex.
R is obviusly an equivalence relation.
Strongly connected components can be defined as the induced graphs of the equivalence classes.
Why are the two definitions equivalent?
CONDENSATION
By the second definition of SCC, SCC partitions the vertices.
DAG
By the first definitiom of SCC, the condensation graph must be a DAG.
There exist a path from u to v iff there exist a path from scc(u) to scc(v) in the condensation graph.
Tarjan again
vi G[N], s;
int scc[N], d[N], low[N], cnt, tin;
void dfs(int u) {
d[u] = low[u] = ++tin;
s.PB(u);
for(int v:G[u]) {
if(!d[v]) {
dfs(v);
low[u] = min(low[u], low[v]);
} else if(!scc[v]) {
low[u] = min(low[u], d[v]);
}
}
if(low[u] == d[u]) {
cnt++;
int t;
do {
t = s.back(); s.pop_back();
scc[t] = cnt;
} while(t != u);
}
}
KOSARAJU'S ALGORITHM
V<pi> G[N];
vi rG[N], order;
int scc[N], vis[N];
void dfs(int u) {
vis[u] = 1;
for(int v:rG[u]) if(!vis[v])
dfs(v);
order.PB(u);
}
void dfs2(int u, int cnt) {
scc[u] = cnt;
d[u] = dis;
for(int v:G[u]) {
if(!scc[v]) dfs2(v, cnt);
}
}
int main() {
int n, m;
cin >> n >> m;
for(int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
G[u].EB(v);
rG[v].EB(u);
}
for(int i = 1; i <= n; i++) if(!vis[i])
dfs(i);
reverse(ALL(order));
int cnt = 0;
for(int i:order) if(!scc[i]) {
dfs2(i, ++cnt);
}
}
CORRECTNESS
如果 u --> v 而且 v --> u,顯然該演算發會把 u, v 放在同一個 SCC 裡,所以只要
如果 u -x-> v 而且 v -x-> u ,顯然該演算發不會把 u, v 放在同一個 SCC 裡
如果 u -x-> v 而且 v --> u,第二次 DFS 的 order u 一定要先 DFS 到,也就是說在反圖裡(u --> v, v -x-> u) DFS 時,要先離開 v 再離開 u,不難發現,這是對的
EXERCISES
蛤 這是圖論喔
EXERCISES
cses 1684 (裸)
課後問題: 想一下3-SAT怎麼做
小試身手一下
好 spaghetti