简介
在一个有向图里,可以有环也可以无环,当一个点u到另一个点v的所有可行路径都必经一个点w,那么可以称u被w支配,所以一个点必定会被一个点支配(除了顶点)
支配树是一个单源算法(学习支配树的时候因为不知道这个被坑了4天,感谢某位大佬抬了一手)
树形图
因为该图本身就是树状,所以这颗树本身就是自己的支配树,显然根节点到任意非根节点都需要经过树路径上的所有点,这些点都是其支配点。
DAG图下建支配树
由于DAG图是有向无环图,所以只需要进行拓扑序建树即可,可以快速得到支配树的上层结构。
例如得到了1-n每个点的拓扑序,也得到了拓扑排序后的点顺序,然后按照拓扑序进行遍历,但遍历到x点的时候,1-(x-1)点的结构已经建立完了,然后通过遍历他的所有父亲节点,得到其中的LCA,然后将他自己连到LCA上,由于是一边建树一边查询LCA,所以用到倍增LCA进行动态LCA。
由于是有向图,所以可以同时存其反向图,遍历反向图进行拓扑排序,正向图进行建树。
Graph参见 Graph图结构体
const int MAX_DEP = 20;
// 注意0,1点的边界问题
struct DominatorTree {
int deg[N]; // 入度
int dep[N]; //
int dfn[N];
int st[N];
int tot;
// 拓扑序, 要保证root是入度为0
void bfs(Graph &gh, int root) {
queue<int> q;
q.push(root);
tot = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
dfn[u] = ++tot;
st[tot] = u;
forg(i, gh.head[u], gh.eg) {
int v = gh.eg[i].e;
if ((--deg[v]) == 0) {
q.push(v);
}
}
}
}
// 倍增2^k的父亲
int fa[N][MAX_DEP];
// 倍增LCA
int lca(int u, int v) {
if (dep[u] > dep[v]) {
swap(u, v);
}
int hu = dep[u], hv = dep[v];
int tu = u, tv = v;
for (int det = hv - hu, i = 0; det; det >>= 1, i++) {
if (det & 1)
tv = fa[tv][i];
}
if (tu == tv) {
return tu;
}
for (int i = MAX_DEP - 1; i >= 0; i--) {
if (fa[tu][i] == fa[tv][i]) {
continue;
}
tu = fa[tu][i];
tv = fa[tv][i];
}
return fa[tu][0];
}
// 动态更新节点的父亲属性
void lineFa(int u, int v) {
fa[u][0] = v;
for (int i = 1; i < MAX_DEP; i++) {
v = fa[u][i] = fa[v][i - 1];
}
}
// 建树, op是gh的反向图,用来寻找其父亲
void build(Graph &gh, Graph &op, int n, int root) {
memcpy(deg, gh.deg, sizeof(int) * (n + 1));
bfs(gh, root);
for (int k = 1; k <= tot; k++) {
int u = st[k], fath = -1;
dep[u] = 0;
for (int i = op.head[u]; ~i; i = op.eg[i].nxt) {
int v = op.eg[i].e;
if (dfn[v] > dfn[u]) continue;
fath = (fath == -1 ? v : lca(fath, v));
}
if (fath == -1) fath = u;
lineFa(u, fath);
dep[u] = dep[fath] + 1;
}
}
} dtree;
有向图
Lengauer-Tarjan算法(待学)
例题
再贴一个被折磨了4天的那道题(顺便学了下倍增LCA),HDU杭电2019暑期多校第三场B题
一个DAG图下每次询问给出的两个点u,v到所有出度为0的点的支配点个数
这题通过反向建边+虚点连接所有出度为0的点建立支配树,每次查询的时候,查询树上的u,v两点的深度,容斥减去其LCA点的深度,就是其支配点的个数
HDU 6604 Blow up the city
// 巨菜的ACMer-Happy233
#include <bits/stdc++.h>
using namespace std;
//-----
typedef double db;
typedef long long ll;
typedef unsigned int ui;
typedef vector<int> vi;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
#define mp make_pair
#define fi first
#define se second
#define pw(x) (1ll << (x))
#define bt(x, i) ((x >> i) & 1)
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define rep(i, l, r) for(int i=(l);i<(r);++i)
#define per(i, l, r) for(int i=(r)-1;i>=(l);--i)
#define sf(x) scanf("%d", &(x))
#ifndef ACM_LOCAL
#define endl '\n'
#endif
const double pi = acos(-1);
const int MOD = int(998244353);
#define forg(i, h, eg) for(int i = (h); ~i; i = (eg[i]).nxt)
struct Edge {
int e, nxt;
ll v;
Edge() = default;
Edge(int a, ll b, int c = 0) : e(a), v(b), nxt(c) {}
bool operator<(const Edge &a) const {
return (a.v == v ? e < a.e : v < a.v);
}
};
const ll INF = ll(1e11);
const int N = int(1e5 + 10);
const int M = int(3e5 + 10);
struct Graph {
Edge eg[M];
int deg[N];
int head[N];
int cnt;
void init(int n) {
memset(head, -1, sizeof(int) * ++n);
memset(deg, 0, sizeof(int) * n);
cnt = 0;
}
inline void addEdge(int x, int y, ll v = 0) {
eg[cnt] = Edge(y, v, head[x]);
head[x] = cnt++;
deg[y]++;
}
};
const int MAX_DEP = 20;
struct DominatorTree {
int deg[N]; // 入度
int dep[N]; //
int dfn[N];
int st[N];
int tot;
// 拓扑序, 要保证root是入度为0
void bfs(Graph &gh, int root) {
queue<int> q;
q.push(root);
tot = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
dfn[u] = ++tot;
st[tot] = u;
forg(i, gh.head[u], gh.eg) {
int v = gh.eg[i].e;
if ((--deg[v]) == 0) {
q.push(v);
}
}
}
}
// 倍增2^k的父亲
int fa[N][MAX_DEP];
// 倍增LCA
int lca(int u, int v) {
if (dep[u] > dep[v]) {
swap(u, v);
}
int hu = dep[u], hv = dep[v];
int tu = u, tv = v;
for (int det = hv - hu, i = 0; det; det >>= 1, i++) {
if (det & 1)
tv = fa[tv][i];
}
if (tu == tv) {
return tu;
}
for (int i = MAX_DEP - 1; i >= 0; i--) {
if (fa[tu][i] == fa[tv][i]) {
continue;
}
tu = fa[tu][i];
tv = fa[tv][i];
}
return fa[tu][0];
}
void lineFa(int u, int v) {
fa[u][0] = v;
for (int i = 1; i < MAX_DEP; i++) {
v = fa[u][i] = fa[v][i - 1];
}
}
void build(Graph &gh, Graph &op, int n, int root) {
memcpy(deg, gh.deg, sizeof(int) * (n + 1));
bfs(gh, root);
for (int k = 1; k <= tot; k++) {
int u = st[k], fath = -1;
dep[u] = 0;
for (int i = op.head[u]; ~i; i = op.eg[i].nxt) {
int v = op.eg[i].e;
if (dfn[v] > dfn[u]) continue;
fath = (fath == -1 ? v : lca(fath, v));
}
if (fath == -1) fath = u;
lineFa(u, fath);
dep[u] = dep[fath] + 1;
}
}
} dtree;
Graph gh, op;
void solve() {
int n, m;
while (cin >> n >> m) {
gh.init(n);
op.init(n);
rep(i, 0, m) {
int a, b;
cin >> a >> b;
gh.addEdge(b, a);
op.addEdge(a, b);
}
for (int i = 1; i <= n; i++) {
if (gh.deg[i] == 0) {
gh.addEdge(0, i);
op.addEdge(i, 0);
}
}
dtree.build(gh, op, n, 0);
int q;
cin >> q;
while (q--) {
int u, v;
cin >> u >> v;
int f = dtree.lca(u, v);
int ans = dtree.dep[u] + dtree.dep[v] - dtree.dep[f] - 1;
cout << ans << endl;
}
}
}
int main() {
#ifdef ACM_LOCAL
freopen("./data/1.in", "r", stdin);
// freopen("./data/std.out", "w", stdout);
#else
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#endif
#ifdef ACM_LOCAL
auto start = clock();
#endif
int t;
cin >> t;
while (t--)
solve();
#ifdef ACM_LOCAL
auto end = clock();
cerr << "Run Time: " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
#endif
return 0;
}