支配树模版题~
这东西似乎非常好写……
但是证明好长好长啊~
$$sdom(u) = min\{\{u|(v, u) \in E, dfn(v) < dfn(u)\}\cup\{sdom(q)|dfn(q)>dfn(u), \exists (p, x) \in E, q \in path(S, p)\}\}$$
前一半是走前向边或者树边,后一半是走横叉边或者返祖边
$$idom(u) = \begin{cases}sdom(u)& \text{ if } sdom(v)= sdom(u)\\ idom(v) & \text{ if } sdom(v)<sdom(u) \end{cases}$$
其中\(v\)代表\((sdom(u), u]\)之中\(sdom\)最小的那一个点。
#include <bits/stdc++.h> using namespace std; #define LL long long const int N = 200000; int n, m; int dfn[N], lst[N], id[N], sd[N], paid[N], mns[N]; int anc[N], par[N], tot; vector <int> bi[N], bp[N], bb[N]; LL sum[N]; void dfs(int t) { dfn[t] = ++ tot; lst[tot] = t; for (int i = 0; i < bi[t].size(); ++ i) if (!dfn[bi[t][i]]) dfs(bi[t][i]), par[bi[t][i]] = t; } int check_min(int a, int b) { if (dfn[a] < dfn[b]) return a; else return b; } int find_anc(int t) { if (anc[t] == t) return t; else { int a = find_anc(anc[t]); if (a != anc[t] && dfn[sd[mns[anc[t]]]] < dfn[sd[mns[t]]]) mns[t] = mns[anc[t]]; return anc[t] = a; } } int main() { while (scanf("%d%d", &n, &m) == 2) { for (int i = 0; i <= n; ++ i) bi[i].clear(), bp[i].clear(), bb[i].clear(); for (int i = 0; i <= n; ++ i) dfn[i] = lst[i] = par[i] = id[i] = sd[i] = paid[i] = sum[i] = 0; for (int i = 1; i <= n; ++ i) anc[i] = mns[i] = i; tot = 0; for (int i = 1; i <= m; ++ i) { int a, b; scanf("%d%d", &a, &b); bi[a].push_back(b); bp[b].push_back(a); } dfs(n); for (int i = n; ; -- i) { for (int j = 0; j < bb[lst[i]].size(); ++ j) { find_anc(bb[lst[i]][j]); if (sd[mns[bb[lst[i]][j]]] == sd[bb[lst[i]][j]]) id[bb[lst[i]][j]] = sd[bb[lst[i]][j]]; else paid[bb[lst[i]][j]] = mns[bb[lst[i]][j]]; } if (i == 1) break; sd[lst[i]] = par[lst[i]]; for (int j = 0; j < bp[lst[i]].size(); ++ j) if (dfn[bp[lst[i]][j]]) { if (dfn[bp[lst[i]][j]] < i) sd[lst[i]] = check_min(sd[lst[i]], bp[lst[i]][j]); else { find_anc(bp[lst[i]][j]); sd[lst[i]] = check_min(sd[lst[i]], sd[mns[bp[lst[i]][j]]]); } } mns[lst[i]] = lst[i]; anc[lst[i]] = par[lst[i]]; bb[sd[lst[i]]].push_back(lst[i]); } for (int i = 1; i <= n; ++ i) if (paid[lst[i]]) id[lst[i]] = id[paid[lst[i]]]; // for (int i = 1; i <= n; ++ i) // printf("%d\n", id[i]); for (int i = 1; i <= n; ++ i) sum[lst[i]] = sum[id[lst[i]]] + lst[i]; for (int i = 1; i <= n; ++ i) printf("%lld%c", sum[i], (i == n? ‘\n‘: ‘ ‘)); } }