问题描述

给出一张有向图,可能存在环,对于所有的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;
}

HDU 4694 Important Sisters

 #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;
}
}

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. xshell替代工具finalShell

    主要特性:1.多平台支持Windows,Mac OS X,Linux2.多标签,批量服务器管理.3.支持登录Ssh和Windows远程桌面.4.漂亮的平滑字体显示,内置100多个配色方案.5.终端,s ...

  2. Ionic App中嵌入外部网页的问题

    在app中不可避免的要引用第三方的页面,那么在Ionic中是如何实现呢? 1.设计引用外部页面的html框架页面,分3部分,表头有2个按钮,中间是引用的页面,底部隐藏分享相关按钮,具体页面如下: &l ...

  3. 浅谈左偏树在OI中的应用

    Preface 可并堆,一个听起来很NB的数据结构,实际上比一般的堆就多了一个合并的操作. 考虑一般的堆合并时,当我们合并时只能暴力把一个堆里的元素一个一个插入另一个堆里,这样复杂度将达到\(\log ...

  4. 线程中join()的用法

    Thread中,join()方法的作用是调用线程等待该线程完成后,才能继续用下运行. public static void main(String[] args) throws Interrupted ...

  5. 大数据处理过程核心技术ETL详细介绍

    架构挑战 1.对现有数据库管理技术的挑战. 2.经典数据库技术并没有考虑数据的多类别(variety).SQL(结构化数据查询语言),在设计的一开始是没有考虑到非结构化数据的存储问题. 3.实时性技术 ...

  6. Ionic 3 延迟加载(Lazy Load)实战(一)

    本文分享并演示了在 Ionic 3 框架中如何进行模块的延迟加载(Lazy Load)开发. 在我的实战课程「快速上手Ionic3 多平台开发企业级问答社区」中,因为开发的仿知乎 App 模块间的加载 ...

  7. python 批量下载图片

    #coding=utf-8import re,sysimport urllib def getHtml(url): page = urllib.urlopen(url) html = page.rea ...

  8. myeclipse快捷方式汇总

    选择你要注释的那一行或多行代码,按Ctrl+/即可,取消注释也是选中之后按Ctrl+/即可. 如果你想使用的快捷键的注释是的话,那么你的快捷键是ctrl+shift+/我以前都是手动注释的,直接打// ...

  9. 嵌入式linux教程

    串口通信minicom $ sudo apt-get install minicom ///安装 # minicom –s //运行 //CTRL+A Z 弹出菜单       2.NFS网络文件配置 ...

  10. eclispe file查找

    今天查找一段js代码时在本页内找不到,所以需要在整个工程下寻找. 过程如下