題目
【模板】支配樹
實現思路
- DFS 一次得到
Dfn
和 Tid
數組;
- 從
Dfn
大的點開始枚舉點 u:
- 枚舉 u 的反向鄰接點 v,則 v 所在的並查集的最小
SDom
可以更新 u(因爲 u 的 DFS 樹上的父親還沒更新進帶權並查集,所以這時候所有的 v 都可以在符合定理的條件下更新 u),注意不要管不在 DFS 樹上的點;
- 在
SDom
樹上將 SDom[u]
向 u 連邊;
- 將 u 更新進帶權並查集(將自己在並查集上連向自己 DFS 樹上的父親);
- 此時 u 所在的子樹的
SDom
已經處理好了,於是找到 u 的 DFS 樹的父親 r,枚舉 r 的 SDom
樹的兒子 v,用求 IDom
的定理得到 v 的 IDom
對應的點(不能直接賦成那個點的 IDom
,因爲那個點可能還沒更新)。
- 從
Dfn
小的點開始枚舉點 u,並把 IDom[u]
由點變爲值,即 IDom[u] = IDom[IDom[u]]
。
代碼
#include <bits/stdc++.h>
const int MAXN = 200000;
const int MAXM = 300000;
int N, M;
std::vector<int> G[MAXN + 5], H[MAXN + 5];
std::vector<int> STree[MAXN + 5], ITree[MAXN + 5];
int DfnCnt;
int Fat[MAXN + 5];
int Dfn[MAXN + 5], Tid[MAXN + 5];
int SDom[MAXN + 5],IDom[MAXN + 5];
struct Union_Find {
int Fat[MAXN + 5], Min[MAXN + 5];
void Init(int n) {
for (int i = 1; i <= n; i++)
Fat[i] = Min[i] = i;
}
int Find(int u) {
if (Fat[u] == u)
return u;
int anc = Find(Fat[u]);
if (Dfn[SDom[Min[u]]] > Dfn[SDom[Min[Fat[u]]]])
Min[u] = Min[Fat[u]];
return Fat[u] = anc;
}
}T;
void Dfs(int u) {
Tid[Dfn[u] = ++DfnCnt] = u;
for (int i = 0; i < int(G[u].size()); i++) {
int v = G[u][i];
if (!Dfn[v])
Fat[v] = u, Dfs(v);
}
}
void Lengauer_Tarjan() {
for (int i = DfnCnt; i >= 2; i--) {
int u = Tid[i];
for (int j = 0; j < int(H[u].size()); j++) {
int v = H[u][j];
if (Dfn[v]) {
T.Find(v);
if (Dfn[SDom[u]] > Dfn[SDom[T.Min[v]]])
SDom[u] = SDom[T.Min[v]];
}
}
STree[SDom[u]].push_back(u);
T.Fat[u] = Fat[u];
int r = Fat[u];
for (int j = 0; j < int(STree[r].size()); j++) {
int v = STree[r][j];
T.Find(v);
if (SDom[T.Min[v]] == r)
IDom[v] = r;
else
IDom[v] = T.Min[v];
}
for (int i = 2; i <= DfnCnt; i++) {
int u = Tid[i];
if (IDom[u] != SDom[u])
IDom[u] = IDom[IDom[u]];
}
}
int Size[MAXN + 5];
void Count(int u) {
Size[u] = 1;
for (int i = 0; i < int(ITree[u].size()); i++) {
int v = ITree[u][i];
Count(v);
Size[u] += Size[v];
}
}
int main() {
scanf("%d%d", &N, &M);
for (int i = 1; i <= M; i++) {
int u, v; scanf("%d%d", &u, &v);
G[u].push_back(v);
H[v].push_back(u);
}
for (int i = 1; i <= N; i++)
SDom[i] = i;
T.Init(N);
Dfs(1);
Lengauer_Tarjan();
for (int i = 2; i <= N; i++)
if (IDom[i])
ITree[IDom[i]].push_back(i);
Count(1);
for (int i = 1; i <= N; i++)
printf("%d ", Size[i]);
return 0;
}