问题描述

给出一张有向图,可能存在环,对于所有的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. Vue-vue-cli初始化项目

    一.下载安装node.js下载地址:https://nodejs.org/en/download/,选择合适自己的版本下载,我下载的是Windows Installer (.msi)    32.bi ...

  2. c# 设置桌面背景窗口 SetParent

    using System; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms ...

  3. SQL跨服务器查询数据库

    有时候一个项目需要用到两个数据库或多个数据库而且这些数据库在不同的服务器上时,就需要通过跨服务器查找数据 在A服务器的数据库a查询服务器B的数据库b 的bb表 假如服务器B的IP地址为:10.0.22 ...

  4. JVM规范系列第1章:引言

    如果你还没下载<Java虚拟机规范>这本书,那么先点击这里下载再一块儿看吧. 前言 Java 虚拟机是一个抽象化的机器,整个规范中提及的 Java 虚拟机都是抽象化的概念,而不是特指 Or ...

  5. ANSYS附加动水质量(westergarrd公式)

    在水工结构的抗震计算中,不可避免的需要考虑动水压力的作用,当前规范中一般是要求将动水压力以附加质量的形式考虑,如果对压力用质量形式考虑有疑惑时,可以这样理解:结构发生振动时,会带动周围的水体发生运动, ...

  6. open-falcon ---客户机agent操作

    open-falcon的agent用于采集机器负载监控指标,比如cpu.idle.load.1min.disk.io.util等等,每隔60秒push给Transfer.agent与Transfer建 ...

  7. C_数据结构_链式二叉树

    # include <stdio.h> # include <malloc.h> struct BTNode { int data; struct BTNode * pLchi ...

  8. PHP输入流 php://input 相关【转】

    为什么xml_rpc服务端读取数据都是通过file_get_contents(‘php://input', ‘r').而不是从$_POST中读取,正是因为xml_rpc数据规格是xml,它的Conte ...

  9. 点评qq浏览器

    1.内核.       qq浏览器用的是是IE8的内核,而且是只有IE内核,所以,在速度上没办法跟那些webkit内核做对比了,不过也没有太慢,在沈航的网速下,打开网页的速度也还是勉强可以接受的.   ...

  10. 20135337——Linux实践三:ELF文件格式(64位系统,简单分析)

    ELF文件格式简单分析 (具体分析见上一篇ELF文件格式32位系统) ELF-header 第一行: 457f 464c :魔数: 0201 :64位系统,小端法 01 :文件头版本 剩余默认0: 第 ...