Dominator Tree & Lengauer-Tarjan Algorithm
问题描述
给出一张有向图,可能存在环,对于所有的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
#include <bits/stdc++.h> using namespace std; typedef long long lnt; const int mxn = ; int n, m; int tim;
int dfn[mxn];
int idx[mxn];
int fat[mxn];
int idm[mxn];
int sdm[mxn];
int anc[mxn];
int tag[mxn];
lnt siz[mxn];
lnt son[mxn]; vector<int> G[mxn];
vector<int> R[mxn];
vector<int> S[mxn];
vector<int> T[mxn]; void dfsG(int u)
{
idx[dfn[u] = ++tim] = u; for (auto v : G[u])if (!dfn[v])
fat[v] = u, dfsG(v);
} void dfsT(int u)
{
siz[u] = ; for (auto v : T[u])
dfsT(v), siz[u] += siz[v];
} int find(int u)
{
if (u == anc[u])
return u; int r = find(anc[u]); if (dfn[sdm[tag[anc[u]]]] < dfn[sdm[tag[u]]])
tag[u] = tag[anc[u]]; return anc[u] = r;
} signed main(void)
{
cin >> n >> m; for (int i = , u, v; i <= m; ++i)
{
cin >> u >> v;
G[u].push_back(v);
R[v].push_back(u);
} for (int i = ; i <= n; ++i)
sdm[i] = tag[i] = anc[i] = i; dfsG(); for (int i = tim; i > ; --i)
{
int u = idx[i]; for (auto v : R[u])if (dfn[v])
{
find(v);
if (dfn[sdm[tag[v]]] < dfn[sdm[u]])
sdm[u] = sdm[tag[v]];
} anc[u] = fat[u]; S[sdm[u]].push_back(u); int t = idx[i - ]; for (auto v : S[t])
{
find(v);
if (sdm[tag[v]] == t)
idm[v] = t;
else
idm[v] = tag[v];
} S[t].clear();
} for (int i = ; i <= tim; ++i)
{
int u = idx[i];
if (idm[u] != sdm[u])
idm[u] = idm[idm[u]];
} for (int i = ; i <= tim; ++i)
T[idm[i]].push_back(i); dfsT(); lnt ans = tim * (tim - ); for (int i = tim, u; i >= ; --i)
{
++son[u = idx[i]];
if (idm[u] != )
son[idm[u]] += son[u];
else
ans -= son[u] * (son[u] - );
} ans >>= ; cout << ans << endl;
}
#include <cstdio>
#include <cstring> #define mxn 50005
#define mxm 200005
#define lnt long long int n, m; struct Lin {
int tt;
int hd[mxn];
int nt[mxm];
int to[mxm]; void init(void) {
memset(hd, , sizeof hd), tt = ;
} void adde(int u, int v) {
nt[++tt] = hd[u], to[tt] = v, hd[u] = tt;
}
}G, R, T, S; int tim;
int idx[mxn];
int dfn[mxn];
int fat[mxn];
int anc[mxn];
int tag[mxn];
int sdm[mxn];
int idm[mxn];
lnt ans[mxn]; void dfsG(int u) {
idx[dfn[u] = ++tim] = u; for (int i = G.hd[u], v; i; i = G.nt[i])
if (!dfn[v = G.to[i]])dfsG(v), fat[v] = u;
} void dfsT(int u) {
ans[u] += u; for (int i = T.hd[u], v; i; i = T.nt[i])
ans[v = T.to[i]] += ans[u], dfsT(v);
} int find(int u) {
if (anc[u] == u)return u; int r = find(anc[u]); if (dfn[sdm[tag[anc[u]]]] < dfn[sdm[tag[u]]])
tag[u] = tag[anc[u]]; return anc[u] = r;
} signed main(void)
{
while (scanf("%d%d", &n, &m) != EOF) {
memset(ans, , sizeof ans);
memset(dfn, , sizeof dfn), tim = ; G.init(); R.init(); T.init(); S.init(); for (int i = , u, v; i <= m; ++i)
scanf("%d%d", &u, &v), G.adde(u, v), R.adde(v, u); for (int i = ; i <= n; ++i)
sdm[i] = tag[i] = anc[i] = i; dfsG(n); for (int i = tim; i > ; --i) {
int u = idx[i], v; for (int j = R.hd[u]; j; j = R.nt[j])
if (dfn[v = R.to[j]]) {
find(v);
if (dfn[sdm[tag[v]]] < dfn[sdm[u]])
sdm[u] = sdm[tag[v]];
} anc[u] = fat[u]; S.adde(sdm[u], u); u = idx[i - ]; for (int j = S.hd[u]; j; j = S.nt[j]) {
find(v = S.to[j]);
if (sdm[tag[v]] == u)
idm[v] = u;
else
idm[v] = tag[v];
}
} for (int i = ; i <= tim; ++i) {
int u = idx[i];
if (idm[u] != sdm[u])
idm[u] = idm[idm[u]];
T.adde(idm[u], u);
} dfsT(n); for (int i = ; i < n; ++i)
printf("%lld ", ans[i]); printf("%lld\n", ans[n]);
}
}
SPOJ BIA - Bytelandian Information Agency
#include <bits/stdc++.h> using namespace std; const int mxn = ;
const int mxm = ; int n, m; vector<int> G[mxn];
vector<int> R[mxn];
vector<int> S[mxn]; inline void init(vector<int> v[mxn])
{
for (int i = ; i < mxn; ++i)
v[i].clear();
} int tim;
int dfn[mxn];
int idx[mxn];
int fat[mxn];
int idm[mxn];
int sdm[mxn];
int anc[mxn];
int cnt[mxn];
int tag[mxn]; void dfsG(int u)
{
idx[dfn[u] = ++tim] = u; for (auto v : G[u])if (!dfn[v])
fat[v] = u, dfsG(v);
} int find(int u)
{
if (anc[u] == u)
return u; int r = find(anc[u]); if (dfn[sdm[tag[anc[u]]]] < dfn[sdm[tag[u]]])
tag[u] = tag[anc[u]]; return anc[u] = r;
} signed main(void)
{
while (cin >> n >> m)
{
init(G);
init(R);
init(S); tim = ; memset(cnt, , sizeof cnt);
memset(dfn, , sizeof dfn); for (int i = , u, v; i <= m; ++i)
{
cin >> u >> v;
G[u].push_back(v);
R[v].push_back(u);
} for (int i = ; i <= n; ++i)
sdm[i] = tag[i] = anc[i] = i; dfsG(); for (int i = tim; i > ; --i)
{
int u = idx[i]; for (auto v : R[u])if (dfn[v])
{
find(v);
if (dfn[sdm[tag[v]]] < dfn[sdm[u]])
sdm[u] = sdm[tag[v]];
} anc[u] = fat[u]; S[sdm[u]].push_back(u); u = idx[i - ]; for (auto v : S[u])
{
find(v); if (sdm[tag[v]] == u)
idm[v] = u;
else
idm[v] = tag[v];
} S[u].clear();
} for (int i = ; i <= tim; ++i)
{
int u = idx[i];
if (idm[u] != sdm[u])
idm[u] = idm[idm[u]];
} for (int i = ; i <= tim; ++i)
++cnt[idm[i]]; int ans = ; for (int i = ; i <= tim; ++i)
if (cnt[i])++ans; cout << ans << endl; for (int i = ; i <= tim; ++i)
if (cnt[i])cout << i << " "; cout << endl;
}
}
@Author: YouSiki
Dominator Tree & Lengauer-Tarjan Algorithm的更多相关文章
- Java内存泄漏分析系列之七:使用MAT的Histogram和Dominator Tree定位溢出源
原文地址:http://www.javatang.com 基础概念 先列出几个基础的概念: Shallow Heap 和 Retained Heap Shallow Heap表示对象本身占用内存的大小 ...
- Tarjan Algorithm
List Tarjan Algorithm List Knowledge 基本知识 基本概念 复杂度 有向图 Code 缩点 Code 用途 无向图 Articulation Point-割顶与连通度 ...
- 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 ...
- 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 ...
- Codeforces 980F Cactus to Tree 仙人掌 Tarjan 树形dp 单调队列
原文链接https://www.cnblogs.com/zhouzhendong/p/CF980F.html 题目传送门 - CF980F 题意 给定一个 $n$ 个节点 $m$ 条长为 $1$ 的边 ...
- Codeforces Round #391 div1 757F (Dominator Tree)
首先先膜杜教orz 这里简单说一下支配树的概念 支配树是对一个有向图来讲的 规定一个起点s,如果s到v的路径上必须经过某些点u,那么离s最近的点u就是v的支配点 在树上的关系就是,v的父亲是u. 一般 ...
- 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 ...
- [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, ...
- 【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 ...
随机推荐
- xshell替代工具finalShell
主要特性:1.多平台支持Windows,Mac OS X,Linux2.多标签,批量服务器管理.3.支持登录Ssh和Windows远程桌面.4.漂亮的平滑字体显示,内置100多个配色方案.5.终端,s ...
- Ionic App中嵌入外部网页的问题
在app中不可避免的要引用第三方的页面,那么在Ionic中是如何实现呢? 1.设计引用外部页面的html框架页面,分3部分,表头有2个按钮,中间是引用的页面,底部隐藏分享相关按钮,具体页面如下: &l ...
- 浅谈左偏树在OI中的应用
Preface 可并堆,一个听起来很NB的数据结构,实际上比一般的堆就多了一个合并的操作. 考虑一般的堆合并时,当我们合并时只能暴力把一个堆里的元素一个一个插入另一个堆里,这样复杂度将达到\(\log ...
- 线程中join()的用法
Thread中,join()方法的作用是调用线程等待该线程完成后,才能继续用下运行. public static void main(String[] args) throws Interrupted ...
- 大数据处理过程核心技术ETL详细介绍
架构挑战 1.对现有数据库管理技术的挑战. 2.经典数据库技术并没有考虑数据的多类别(variety).SQL(结构化数据查询语言),在设计的一开始是没有考虑到非结构化数据的存储问题. 3.实时性技术 ...
- Ionic 3 延迟加载(Lazy Load)实战(一)
本文分享并演示了在 Ionic 3 框架中如何进行模块的延迟加载(Lazy Load)开发. 在我的实战课程「快速上手Ionic3 多平台开发企业级问答社区」中,因为开发的仿知乎 App 模块间的加载 ...
- python 批量下载图片
#coding=utf-8import re,sysimport urllib def getHtml(url): page = urllib.urlopen(url) html = page.rea ...
- myeclipse快捷方式汇总
选择你要注释的那一行或多行代码,按Ctrl+/即可,取消注释也是选中之后按Ctrl+/即可. 如果你想使用的快捷键的注释是的话,那么你的快捷键是ctrl+shift+/我以前都是手动注释的,直接打// ...
- 嵌入式linux教程
串口通信minicom $ sudo apt-get install minicom ///安装 # minicom –s //运行 //CTRL+A Z 弹出菜单 2.NFS网络文件配置 ...
- eclispe file查找
今天查找一段js代码时在本页内找不到,所以需要在整个工程下寻找. 过程如下