支配树学习思路/模板
首先解释什么是支配树。 首先我们先看一下DAG,我们现在有一个DAG: 如果我们从1出发,对于图中的一个点1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void add(int x, int y)
{
ver[++tot] = y;
ne[tot] = he[x];
he[x] = tot;
verf[tot] = x;
nef[tot] = hef[y];
hef[y] = tot;
}
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
int x;
while(scanf("%d", &x), x)
{di[i]++;add(x, i);}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18void topu()
{
for (int i = 1; i <= n; i++) if (!di[i])
q.push(i);
while(q.size())
{
int now = q.front();
q.pop();
topp[++cnt] = now;//记录拓扑序
for (int i = he[now]; i; i = ne[i])
{
int y = ver[i];
di[y]--;
if (!di[y])
q.push(y);
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17void adda(int x, int y)
{
vera[++tot] = y;
nea[tot] = hea[x];
hea[x] = tot;
}
for (int i = 1; i <= n; i++)
{
int x = verf[hef[topp[i]]];//利用反图,找到topp[i]点的入点
for (int j = hef[topp[i]]; j; j = nef[j])
x = lca(x, verf[j]);//遍历所有入点,算出lca
adda(x, topp[i]);//联边建树
d[topp[i]] = d[x] + 1;
f[topp[i]][0] = x;
for (int j = 1; j <= 25; j++)
f[topp[i]][j] = f[f[topp[i]][j-1]][j-1];//边建树便记录f数组
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cstdlib>
#define ll long long
using namespace std;
const int N = 2e5+5;
int tot, he[N], ver[N], ne[N];
int hef[N], verf[N], nef[N];
int tota, hea[N], vera[N], nea[N];
int f[N][30], d[N];
int di[N];
int n,m, cnt, topp[N];
queue<int> q;
void add(int x, int y)
{
ver[++tot] = y;
ne[tot] = he[x];
he[x] = tot;
verf[tot] = x;
nef[tot] = hef[y];
hef[y] = tot;
}
void adda(int x, int y)
{
vera[++tot] = y;
nea[tot] = hea[x];
hea[x] = tot;
}
void topu()
{
for (int i = 1; i <= n; i++) if (!di[i])
q.push(i);
while(q.size())
{
int now = q.front();
q.pop();
topp[++cnt] = now;
for (int i = he[now]; i; i = ne[i])
{
int y = ver[i];
di[y]--;
if (!di[y])
q.push(y);
}
}
}
int lca(int x, int y)
{
if (d[x] > d[y]) swap(x, y);
for (int i = 25; i >= 0; i--)
{
if (d[f[y][i]] < d[x]) continue;
y = f[y][i];
}
if (x == y) return x;
for (int i = 25; i >= 0; i--)
if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
int siz[N];
int dfs(int now)
{
siz[now] = 1;
for (int i = hea[now]; i; i = nea[i])
{
siz[now] += dfs(vera[i]);
}
return siz[now];
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
int x;
while(scanf("%d", &x), x)
{di[i]++;add(x, i);}
}
topu();
for (int i = 1; i <= n; i++)
{
int x = verf[hef[topp[i]]];
for (int j = hef[topp[i]]; j; j = nef[j])
x = lca(x, verf[j]);
adda(x, topp[i]);
d[topp[i]] = d[x] + 1;
f[topp[i]][0] = x;
for (int j = 1; j <= 25; j++)
f[topp[i]][j] = f[f[topp[i]][j-1]][j-1];
}
dfs(0);
for (int i = 1; i <= n; i++)
printf("%d\n", siz[i]-1);
return 0;
}
很好,我们现在探讨完DAG的情况了,终于进入最普遍的支配树的学习了。即:一般有向图的支配树与支配点(即必经点)。 下面有一个有向图: (蓝色边为其dfs搜索树的边,为方便每个点的编号都是它的dfn) 这里我引入半支配点的概念:对于图,若存在一个简单路径<x,y>。如果从x到y所经历的所有点(除了x,y)的时间戳都大于y的时间戳,我们认为x为y的半支配点 例如图中的6号点,他的半支配点集为V = {4,7,8,1}。因为从这些点出发都可以找到一个简单路径到达6,且满足上述条件(如图红边所示): 对于每一个点,我们找到他的半支配点集中的dfn最小的一个,记做
这里给出几个性质: 1、每个点的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132// % everyone
#include <cstdio>
#include<iostream>
#include<cstring>
#include <map>
#include <queue>
#include <set>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <bitset>
#include <array>
#include <cctype>
#include <unordered_map>
#include <time.h>
namespace OI {
double start_time = 0.0;
void read_f(int flag = 0) { freopen("0.in", "r", stdin); if(!flag) freopen("0.out", "w", stdout); }
void fast_cin() { std::ios::sync_with_stdio(false); std::cin.tie(); }
void run_time() { std::cout << "\nESC in : " << ( clock() - start_time ) * 1000.0 / CLOCKS_PER_SEC << "ms" << std::endl; }
void ct() { start_time = clock(); return; }
}
using namespace OI;
template <typename T>
bool bacmp(const T & a, const T & b) { return a > b; }
template <typename T>
bool pecmp(const T & a, const T & b) { return a < b; }
#define ll long long
#define ull unsigned ll
#define _min(x, y) ((x)>(y)?(y):(x))
#define _max(x, y) ((x)>(y)?(x):(y))
#define max3(x, y, z) ( max( (x), max( (y), (z) ) ) )
#define min3(x, y, z) ( min( (x), min( (y), (z) ) ) )
#define pr make_pair
#define pb push_back
using namespace std;
const int N = 2e5+5;
int n, m;
struct Node
{
int tot = 0;
int ver[N<<1], he[N], ne[N<<1];
void add(int x, int y)
{
ver[++tot] = y;
ne[tot] = he[x];
he[x] = tot;
}
}a, b, c, d;
int dfn[N], id[N], fa[N], cnt;
void dfs(int u)
{
dfn[u] = ++cnt; id[cnt] = u;
for (int i = a.he[u]; i; i = a.ne[i])
{
int y = a.ver[i];
if (dfn[y]) continue;
fa[y] = u; dfs(y);
}
}
int semi[N], idom[N], bel[N], val[N];
int fi(int x)
{
if (x == bel[x]) return x;
int fs = fi(bel[x]);
if (dfn[semi[ val[bel[x] ] ] ] < dfn[ semi[val[x] ] ]) val[x] = val[bel[x]];
return bel[x] = fs;
}
void tarjan()
{
for (int s = cnt; s>1; s--)
{
int u = id[s];
for (int i = b.he[u]; i; i = b.ne[i])
{
int y = b.ver[i];
fi(y);
if (dfn[semi[val[y]]] < dfn[semi[u]]) semi[u] = semi[val[y]];
}
c.add(semi[u], u);
bel[u] = fa[u];
u = fa[u];
for (int i = c.he[u]; i; i = c.ne[i])
{
int y = c.ver[i];
fi(y);
if (semi[val[y]] == u) idom[y] = u;
else idom[y] = val[y];
}
}
for (int i = 2; i <= cnt; i++)
{
int u = id[i];
if (idom[u] != semi[u])
idom[u] = idom[idom[u]];
}
}
int ans[N];
void dfs_ans(int u)
{
ans[u] = 1;
for (int i = d.he[u]; i; i = d.ne[i])
{
int y = d.ver[i];
dfs_ans(y);
ans[u] += ans[y];
}
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int x, y; scanf("%d%d", &x, &y);
a.add(x, y);
b.add(y, x);
}
for (int i = 1; i <= n; i++) semi[i] = bel[i] = val[i] = i;
dfs(1);
tarjan();
for (int i = 2; i <= n; i++) d.add(idom[i], i);
dfs_ans(1);
for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
puts("");
return 0;
}