问题描述

给出一张有向图,可能存在环,对于所有的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. Perhaps you are running on a JRE rather than a JDK

    在Eclipse中跑maven项目时,出现上面的问题: 1.有可能你的环境变量配置是在jre上面的,所以你要检查一下你配置文件,PATH和CLASSPATH都要检查 2.eclipse默认是跑在jre ...

  2. WebApi 异步请求(HttpClient)

    还是那几句话: 学无止境,精益求精 十年河东,十年河西,莫欺少年穷 学历代表你的过去,能力代表你的现在,学习代表你的将来 废话不多说,直接进入正题: 今天公司总部要求各个分公司把短信接口对接上,所谓的 ...

  3. EZ 2018 07 06 NOIP模拟赛

    又是慈溪那边给的题目,这次终于没有像上次那样尴尬了, T1拿到了较高的暴力分,T2没写炸,然后T3写了一个优雅的暴力就203pts,Rank3了. 听说其它学校的分数普遍100+,那我们学校还不是强到 ...

  4. 12.4 开课三个月(phpcms安装)

    cms的样式有很多种,我们学习的是phpcms,这些cms都是大同小异,学会了一种就可以使用其它的cms. PHPCMS是一款网站管理软件.该软件采用模块化开发,支持多种分类方式,使用它可方便实现个性 ...

  5. 《Head First 设计模式》例子的C++实现(1 策略模式)

    最近在学习设计模式,用的是 <Head First 设计模式>这本书.感觉这本书写的还是很不错的,深入浅出的介绍了各种常用的设计模式.唯一有点不方便的地方是这本书的例子全都是用的 Java ...

  6. .Net架构篇:思考如何设计一款实用的分布式监控系统?

    前言 无论从最早期的unix操作系统,还是曾经大行其道的单体式应用,还是现在日益流行的微服务架构,始终都离不开监控的身影.如windows的任务管理器,linux的top命令,都可以看作是监控的面板. ...

  7. UWP简单示例(三):快速开发2D游戏引擎

    准备 IDE:Visual Studio 图形 API:Win2D MSDN 教程:UWP游戏开发 游戏开发涉及哪些技术? 游戏开发是一门复杂的艺术,编码方面你需要考虑图形.输入和网络 以及相对独立的 ...

  8. Scrum Meeting 8

                第八次会议 No_00:工作情况 No_01:任务说明 待完成 已完成 No_10:燃尽图 No_11:照片记录 待更新 No_100:代码/文档签入记录 No_101:出席表 ...

  9. LINUX第三次实践:程序破解

    LINUX第三次实践:程序破解 标签(空格分隔): 20135328陈都 一.掌握NOP.JNE.JE.JMP.CMP汇编指令的机器码 NOP:NOP指令即"空指令".执行到NOP ...

  10. 使用composer遇到的问题及解决方法

    可以尝试利用composer下载Yii框架,编辑composer.json文件: { "require":{ "yiisoft/yii2":"~2.0 ...