问题描述
给出一张有向图,可能存在环,对于所有的i,求出从1号点到i点的所有路径上的必经点集合。
什么是支配树
两个简单的小性质——
1.如果i是j的必经点,而j又是k的必经点,则i也是k的必经点。
2.如果i和j都是k的必经点,则i和j之间必然存在必经点关系,不可能互相都不是必经点。
不难发现所有的必经点关系形成了一个以1点为根的树形关系,每个点的支配点集合就是其到根节点(1点)路径上的点集,称这棵树为支配树。
怎么求支配树
假如我们得到的是一个有向无环图,那么只需要$O(N)$的做一遍拓扑排序就可以了,非常简单。
假如我们得到了一张有向有环图,那么我们可以$O(N)$的枚举一个点,把它从图上删去,从根$O(M)$的DFS(或BFS)一次,就可以知道它是哪些点的必经点,复杂度$O(NM)$,简单粗暴,但时间复杂度难以接受。
然后就有了Lengauer-Tarjan算法,复杂度为$O(NlogN)$,有一堆定理证明,想详细的搞明白最好去看Tarjan的英文论文,网上有些中文翻译难免带些小错误。
简单的上手题
据某位大佬说,这个算法还没见到过不是裸题的题…… OTZ
不过确实,目前这个算法一般应用在浅层,题面也是非常的裸,简直就是再说“快来拿支配树上我啊!”
CodeChef Counting on a directed graph GRAPHCNT
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long lnt; 6 7 const int mxn = 100005; 8 9 int n, m; 10 11 int tim; 12 int dfn[mxn]; 13 int idx[mxn]; 14 int fat[mxn]; 15 int idm[mxn]; 16 int sdm[mxn]; 17 int anc[mxn]; 18 int tag[mxn]; 19 lnt siz[mxn]; 20 lnt son[mxn]; 21 22 vector<int> G[mxn]; 23 vector<int> R[mxn]; 24 vector<int> S[mxn]; 25 vector<int> T[mxn]; 26 27 void dfsG(int u) 28 { 29 idx[dfn[u] = ++tim] = u; 30 31 for (auto v : G[u])if (!dfn[v]) 32 fat[v] = u, dfsG(v); 33 } 34 35 void dfsT(int u) 36 { 37 siz[u] = 1; 38 39 for (auto v : T[u]) 40 dfsT(v), siz[u] += siz[v]; 41 } 42 43 int find(int u) 44 { 45 if (u == anc[u]) 46 return u; 47 48 int r = find(anc[u]); 49 50 if (dfn[sdm[tag[anc[u]]]] < dfn[sdm[tag[u]]]) 51 tag[u] = tag[anc[u]]; 52 53 return anc[u] = r; 54 } 55 56 signed main(void) 57 { 58 cin >> n >> m; 59 60 for (int i = 1, u, v; i <= m; ++i) 61 { 62 cin >> u >> v; 63 G[u].push_back(v); 64 R[v].push_back(u); 65 } 66 67 for (int i = 1; i <= n; ++i) 68 sdm[i] = tag[i] = anc[i] = i; 69 70 dfsG(1); 71 72 for (int i = tim; i > 1; --i) 73 { 74 int u = idx[i]; 75 76 for (auto v : R[u])if (dfn[v]) 77 { 78 find(v); 79 if (dfn[sdm[tag[v]]] < dfn[sdm[u]]) 80 sdm[u] = sdm[tag[v]]; 81 } 82 83 anc[u] = fat[u]; 84 85 S[sdm[u]].push_back(u); 86 87 int t = idx[i - 1]; 88 89 for (auto v : S[t]) 90 { 91 find(v); 92 if (sdm[tag[v]] == t) 93 idm[v] = t; 94 else 95 idm[v] = tag[v]; 96 } 97 98 S[t].clear(); 99 } 100 101 for (int i = 2; i <= tim; ++i) 102 { 103 int u = idx[i]; 104 if (idm[u] != sdm[u]) 105 idm[u] = idm[idm[u]]; 106 } 107 108 for (int i = 2; i <= tim; ++i) 109 T[idm[i]].push_back(i); 110 111 dfsT(1); 112 113 lnt ans = tim * (tim - 1); 114 115 for (int i = tim, u; i >= 2; --i) 116 { 117 ++son[u = idx[i]]; 118 if (idm[u] != 1) 119 son[idm[u]] += son[u]; 120 else 121 ans -= son[u] * (son[u] - 1); 122 } 123 124 ans >>= 1; 125 126 cout << ans << endl; 127 }