问题描述

给出一张有向图,可能存在环,对于所有的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 = ;
  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] = ;
  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 = , 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 = ; i <= n; ++i)
  68. sdm[i] = tag[i] = anc[i] = i;
  69.  
  70. dfsG();
  71.  
  72. for (int i = tim; i > ; --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 - ];
  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 = ; 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 = ; i <= tim; ++i)
  109. T[idm[i]].push_back(i);
  110.  
  111. dfsT();
  112.  
  113. lnt ans = tim * (tim - );
  114.  
  115. for (int i = tim, u; i >= ; --i)
  116. {
  117. ++son[u = idx[i]];
  118. if (idm[u] != )
  119. son[idm[u]] += son[u];
  120. else
  121. ans -= son[u] * (son[u] - );
  122. }
  123.  
  124. ans >>= ;
  125.  
  126. cout << ans << endl;
  127. }

HDU 4694 Important Sisters

  1. #include <cstdio>
  2. #include <cstring>
  3.  
  4. #define mxn 50005
  5. #define mxm 200005
  6. #define lnt long long
  7.  
  8. int n, m;
  9.  
  10. struct Lin {
  11. int tt;
  12. int hd[mxn];
  13. int nt[mxm];
  14. int to[mxm];
  15.  
  16. void init(void) {
  17. memset(hd, , sizeof hd), tt = ;
  18. }
  19.  
  20. void adde(int u, int v) {
  21. nt[++tt] = hd[u], to[tt] = v, hd[u] = tt;
  22. }
  23. }G, R, T, S;
  24.  
  25. int tim;
  26. int idx[mxn];
  27. int dfn[mxn];
  28. int fat[mxn];
  29. int anc[mxn];
  30. int tag[mxn];
  31. int sdm[mxn];
  32. int idm[mxn];
  33. lnt ans[mxn];
  34.  
  35. void dfsG(int u) {
  36. idx[dfn[u] = ++tim] = u;
  37.  
  38. for (int i = G.hd[u], v; i; i = G.nt[i])
  39. if (!dfn[v = G.to[i]])dfsG(v), fat[v] = u;
  40. }
  41.  
  42. void dfsT(int u) {
  43. ans[u] += u;
  44.  
  45. for (int i = T.hd[u], v; i; i = T.nt[i])
  46. ans[v = T.to[i]] += ans[u], dfsT(v);
  47. }
  48.  
  49. int find(int u) {
  50. if (anc[u] == u)return u;
  51.  
  52. int r = find(anc[u]);
  53.  
  54. if (dfn[sdm[tag[anc[u]]]] < dfn[sdm[tag[u]]])
  55. tag[u] = tag[anc[u]];
  56.  
  57. return anc[u] = r;
  58. }
  59.  
  60. signed main(void)
  61. {
  62. while (scanf("%d%d", &n, &m) != EOF) {
  63. memset(ans, , sizeof ans);
  64. memset(dfn, , sizeof dfn), tim = ;
  65.  
  66. G.init(); R.init(); T.init(); S.init();
  67.  
  68. for (int i = , u, v; i <= m; ++i)
  69. scanf("%d%d", &u, &v), G.adde(u, v), R.adde(v, u);
  70.  
  71. for (int i = ; i <= n; ++i)
  72. sdm[i] = tag[i] = anc[i] = i;
  73.  
  74. dfsG(n);
  75.  
  76. for (int i = tim; i > ; --i) {
  77. int u = idx[i], v;
  78.  
  79. for (int j = R.hd[u]; j; j = R.nt[j])
  80. if (dfn[v = R.to[j]]) {
  81. find(v);
  82. if (dfn[sdm[tag[v]]] < dfn[sdm[u]])
  83. sdm[u] = sdm[tag[v]];
  84. }
  85.  
  86. anc[u] = fat[u]; S.adde(sdm[u], u); u = idx[i - ];
  87.  
  88. for (int j = S.hd[u]; j; j = S.nt[j]) {
  89. find(v = S.to[j]);
  90. if (sdm[tag[v]] == u)
  91. idm[v] = u;
  92. else
  93. idm[v] = tag[v];
  94. }
  95. }
  96.  
  97. for (int i = ; i <= tim; ++i) {
  98. int u = idx[i];
  99. if (idm[u] != sdm[u])
  100. idm[u] = idm[idm[u]];
  101. T.adde(idm[u], u);
  102. }
  103.  
  104. dfsT(n);
  105.  
  106. for (int i = ; i < n; ++i)
  107. printf("%lld ", ans[i]);
  108.  
  109. printf("%lld\n", ans[n]);
  110. }
  111. }

SPOJ BIA - Bytelandian Information Agency

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std;
  4.  
  5. const int mxn = ;
  6. const int mxm = ;
  7.  
  8. int n, m;
  9.  
  10. vector<int> G[mxn];
  11. vector<int> R[mxn];
  12. vector<int> S[mxn];
  13.  
  14. inline void init(vector<int> v[mxn])
  15. {
  16. for (int i = ; i < mxn; ++i)
  17. v[i].clear();
  18. }
  19.  
  20. int tim;
  21. int dfn[mxn];
  22. int idx[mxn];
  23. int fat[mxn];
  24. int idm[mxn];
  25. int sdm[mxn];
  26. int anc[mxn];
  27. int cnt[mxn];
  28. int tag[mxn];
  29.  
  30. void dfsG(int u)
  31. {
  32. idx[dfn[u] = ++tim] = u;
  33.  
  34. for (auto v : G[u])if (!dfn[v])
  35. fat[v] = u, dfsG(v);
  36. }
  37.  
  38. int find(int u)
  39. {
  40. if (anc[u] == u)
  41. return u;
  42.  
  43. int r = find(anc[u]);
  44.  
  45. if (dfn[sdm[tag[anc[u]]]] < dfn[sdm[tag[u]]])
  46. tag[u] = tag[anc[u]];
  47.  
  48. return anc[u] = r;
  49. }
  50.  
  51. signed main(void)
  52. {
  53. while (cin >> n >> m)
  54. {
  55. init(G);
  56. init(R);
  57. init(S);
  58.  
  59. tim = ;
  60.  
  61. memset(cnt, , sizeof cnt);
  62. memset(dfn, , sizeof dfn);
  63.  
  64. for (int i = , u, v; i <= m; ++i)
  65. {
  66. cin >> u >> v;
  67. G[u].push_back(v);
  68. R[v].push_back(u);
  69. }
  70.  
  71. for (int i = ; i <= n; ++i)
  72. sdm[i] = tag[i] = anc[i] = i;
  73.  
  74. dfsG();
  75.  
  76. for (int i = tim; i > ; --i)
  77. {
  78. int u = idx[i];
  79.  
  80. for (auto v : R[u])if (dfn[v])
  81. {
  82. find(v);
  83. if (dfn[sdm[tag[v]]] < dfn[sdm[u]])
  84. sdm[u] = sdm[tag[v]];
  85. }
  86.  
  87. anc[u] = fat[u];
  88.  
  89. S[sdm[u]].push_back(u);
  90.  
  91. u = idx[i - ];
  92.  
  93. for (auto v : S[u])
  94. {
  95. find(v);
  96.  
  97. if (sdm[tag[v]] == u)
  98. idm[v] = u;
  99. else
  100. idm[v] = tag[v];
  101. }
  102.  
  103. S[u].clear();
  104. }
  105.  
  106. for (int i = ; i <= tim; ++i)
  107. {
  108. int u = idx[i];
  109. if (idm[u] != sdm[u])
  110. idm[u] = idm[idm[u]];
  111. }
  112.  
  113. for (int i = ; i <= tim; ++i)
  114. ++cnt[idm[i]];
  115.  
  116. int ans = ;
  117.  
  118. for (int i = ; i <= tim; ++i)
  119. if (cnt[i])++ans;
  120.  
  121. cout << ans << endl;
  122.  
  123. for (int i = ; i <= tim; ++i)
  124. if (cnt[i])cout << i << " ";
  125.  
  126. cout << endl;
  127. }
  128. }

Useful Roads

@Author: YouSiki

Dominator Tree & Lengauer-Tarjan Algorithm的更多相关文章

  1. Java内存泄漏分析系列之七:使用MAT的Histogram和Dominator Tree定位溢出源

    原文地址:http://www.javatang.com 基础概念 先列出几个基础的概念: Shallow Heap 和 Retained Heap Shallow Heap表示对象本身占用内存的大小 ...

  2. Tarjan Algorithm

    List Tarjan Algorithm List Knowledge 基本知识 基本概念 复杂度 有向图 Code 缩点 Code 用途 无向图 Articulation Point-割顶与连通度 ...

  3. SPOJ 10628 Count on a tree(Tarjan离线LCA+主席树求树上第K小)

    COT - Count on a tree #tree You are given a tree with N nodes.The tree nodes are numbered from 1 to  ...

  4. SPOJ 10628 Count on a tree(Tarjan离线 | RMQ-ST在线求LCA+主席树求树上第K小)

    COT - Count on a tree #tree You are given a tree with N nodes.The tree nodes are numbered from 1 to  ...

  5. Codeforces 980F Cactus to Tree 仙人掌 Tarjan 树形dp 单调队列

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF980F.html 题目传送门 - CF980F 题意 给定一个 $n$ 个节点 $m$ 条长为 $1$ 的边 ...

  6. Codeforces Round #391 div1 757F (Dominator Tree)

    首先先膜杜教orz 这里简单说一下支配树的概念 支配树是对一个有向图来讲的 规定一个起点s,如果s到v的路径上必须经过某些点u,那么离s最近的点u就是v的支配点 在树上的关系就是,v的父亲是u. 一般 ...

  7. MST(Kruskal’s Minimum Spanning Tree Algorithm)

    You may refer to the main idea of MST in graph theory. http://en.wikipedia.org/wiki/Minimum_spanning ...

  8. [LeetCode] Verify Preorder Serialization of a Binary Tree 验证二叉树的先序序列化

    One way to serialize a binary tree is to use pre-oder traversal. When we encounter a non-null node, ...

  9. 【LeetCode】Verify Preorder Serialization of a Binary Tree(331)

    1. Description One way to serialize a binary tree is to use pre-order traversal. When we encounter a ...

随机推荐

  1. Ionic2 App Import BrowserAnimationsModule or NoopAnimationsModule问题

    在开发app的过程中遇见了动画相关方面的问题,具体如下: 解决方法是:在app.module.ts模块中引入BrowserAnimationsModule import { BrowserAnimat ...

  2. [Spark][python]从 web log 中提取出 UserID 作为key 值,形成新的 RDD

    针对RDD, 使用 keyBy 来构筑 key-line 对: [training@localhost ~]$ cat webs.log 56.31.230.188 - 90700 "GET ...

  3. SAAS云平台搭建札记: (二) Linux Ubutu下.Net Core整套运行环境的搭建

    最近做的项目,由于预算有限,公司决定不采购Windows服务器,而采购基于Linux的服务器. 一般的VPS服务器,如果使用Windows系统,那么Windows Server2012\2016安装好 ...

  4. 【译】快速起步-JSX简介

    react version: 15.5.0 快速起步-JSX简介 思考这个变量申明: const element = <h1>Hello, world!</h1>; 这个有趣的 ...

  5. C#获取当前堆栈的各调用方法列表

    在使用.NET编写的代码在debug时很容易进行排查和定位问题,一旦项目上线并出现问题的话那么只能依靠系统日志来进行问题排查和定位,但当项目复杂时,即各种方法间相互调用将导致要获取具体的出错方法或调用 ...

  6. MongoDB日常运维操作命令小结

    总所周知,MongoDB是一个NoSQL非数据库系统,即一个数据库可以包含多个集合(Collection),每个集合对应于关系数据库中的表:而每个集合中可以存储一组由列标识的记录,列是可以自由定义的, ...

  7. Centos7.3下安装Jumpserver 1.0.0(支持windows组件)

    Jumpserver最新版本支持windows组件,废话不多介绍了,下面直接介绍下部署过程: 0)系统环境 CentOS 7.3 IP: 192.168.10.210 [root@jumpserver ...

  8. Week7阅读笔记

    关于银弹: Brooks在他最著名的这篇文章里指出,在软件开发过程里是没有万能的终杀性武器的,只有各种方法综合运用,才是解决之道.而各种声称如何如何神奇的理论或方法,都不是能杀死“软件危机”这头人狼的 ...

  9. Linux课题实践一

    Linux课题实践一 20135318 刘浩晨 1.1应用安装 (1)掌握软件源的维护方法,配置系统使用软件源镜像  删除过期或者重复的软件包:进入”系统设置“-”软件和更新”-”ubuntu软件“- ...

  10. The Last Reader Response——13-17

    Hi, everybody, nice to meet you, this is probably my best and meet you at a time, in the past a seme ...