LCA算法

学习LCA算法

dfs+st在线LCA

  1. 先求一遍dfs序
  2. 然后预处理深度的rmq
  3. 最后进行O(1)的查询

poj1330

poj1330

题意

给一颗树形结构,最后问一个点对的最近公共祖先

题解

在线lca, O(1)的进行查询。

ac code

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
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <cstring>
#include <vector>
#include <cstdlib>
using namespace std;
const int maxn = 1e4+10;
#define push_back pb
#define make_pair mp
#define first X
#define second Y
int n;
struct Edge{
int to, next;
}edge[maxn<<1];
int tot, head[maxn];
bool is_root[maxn];
int F[maxn*2];
int p[maxn];//第一次出现的下标
int cnt;
int rmq[maxn*2];
struct ST{
int mm[maxn<<1];
int dp[maxn<<1][20];
void init(int n){
mm[0] = -1;
for(int i=1; i<=n; i++){
mm[i] = ((i)&(i-1))==0?mm[i-1]+1:mm[i-1];
dp[i][0] = i;
}
for(int j=1; j<=mm[n]; j++){
for(int i=1; i+(1<<j)-1<=n; i++){
dp[i][j] = rmq[dp[i][j-1]]<rmq[dp[i+(1<<(j-1))][j-1]]?
dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
}
}
}
int query(int a, int b){
if(a>b) swap(a, b);
int k = mm[b-a+1];
return rmq[dp[a][k]]<=rmq[dp[b-(1<<k)+1][k]]?
dp[a][k]:dp[b-(1<<k)+1][k];
}
}st;
void init(){
tot = 0;
memset(head, -1, sizeof(head));
}
void add_edge(int u, int v){
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
}
void dfs(int u, int pre, int dep){
F[++cnt] = u;
rmq[cnt] = dep;
p[u] = cnt;
for(int i=head[u]; ~i; i=edge[i].next){
int v = edge[i].to;
if(v == pre) continue;
dfs(v, u, dep+1);
F[++cnt] = u;
rmq[cnt] = dep;
}
}
void lca_init(int rt, int n){
cnt = 0;
dfs(rt, rt, 0);
st.init(2*n);
}
int lca_query(int u, int v){
return F[st.query(p[u], p[v])];
}
int main(){
int _;
scanf("%d", &_);
while(_--){
scanf("%d", &n);
init();
int u, v;
for(int i=0; i<n-1; i++){
scanf("%d%d", &u, &v);
add_edge(u, v);
add_edge(v, u);
is_root[v] = true;
}
int rt = -1;
for(int i=1; i<=n; i++){
if(!is_root[i]){
rt = i;
break;
}
}
lca_init(rt, n);
scanf("%d%d", &u, &v);
printf("%d\n", lca_query(u, v));
}
return 0;
}

离线Tarjan算法

时间复杂度是O(n)的

  1. 从叶子节点逐渐的向上更新答案
  2. ancester[]记录的是真正的父亲节点。

##

poj 1470

ac code

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 1e3+10;
const int maxq = 5e5+10;
bool flag[maxn];
int n;
int count_num[maxq];
int answer[maxq];
bool vis[maxn];
struct Node{
int to, next;
}edge[maxn*2];
struct Query{
int to, next;
int index;
}q[maxq*2];
int tot, head[maxn];
int tt, h[maxn];
int fa[maxn];
//真正的祖先
int ancester[maxn];
int find_fa(int x){
int a = x;
while(x!=fa[x]) x = fa[x];
while(a!=fa[x]) { int z = a; a = fa[a]; fa[z] = x;
}
return x;
}
void uni(int u, int v){
int fau = find_fa(u);
int fav = find_fa(v);
if(fau!=fav){
fa[fau] = fav;
}
}
void init(){
tot = 0;
memset(head, -1, sizeof(head));
memset(flag, 0, sizeof(flag));
memset(h, -1, sizeof(h));
tt = 0;
for(int i=0; i<maxn; i++) fa[i] = i;
memset(ancester, 0, sizeof(ancester));
memset(vis, 0, sizeof(vis));
}
void add_edge(int u, int v){
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
}
void add_query(int u, int v, int index){
q[tt].to = v;
q[tt].next = h[u];
q[tt].index = index;
h[u] = tt++;
q[tt].to = u;
q[tt].next = h[v];
q[tt].index = index;
h[v] = tt++;
}
void LCA(int u){
vis[u] = true;
ancester[u] = u;
for(int i=head[u]; ~i; i=edge[i].next){
int v = edge[i].to;
if(vis[v]) continue;
LCA(v);
for(int i=0; i<=n; i++) find_fa(i);
uni(u, v);
//可能将儿子节点当成自己的fa[u], 这样就可以更新真正的父亲节点了;
//还有一种情况就是根就是父亲节点,这样ancester更新完依旧是自己
ancester[find_fa(u)] = u;
}
for(int i=h[u]; ~i; i = q[i].next){
int v = q[i].to;
if(vis[v]){
answer[q[i].index] = ancester[find_fa(v)];
}
}
}
int main()
{
//ios::sync_with_stdio(false);
while(scanf("%d", &n) != EOF){
int u, num, v;
init();
for(int i=0; i<n; i++){
scanf("%d:(%d)", &u, &num);
//printf("%d %d\n", u, num);
for(int j=0; j<num; j++){
scanf("%d", &v);
add_edge(u, v);
add_edge(v, u);
flag[v] = true;
}
}
int q;
scanf("%d", &q);
//cout<<q<<" "<<u<<" "<<v<<endl;
for(int i=0; i<q; i++){
char ch;
while(ch = getchar())
if(ch == '('){
scanf("%d %d",&u,&v);
ch = getchar();
break;
}
// char op;
// cin>>op;
// scanf("%d %d)", &u, &v);
//printf("input %d %d\n", u, v);
add_query(u, v, i);
}
int rt=1;
for(int i=1; i<=n; i++){
if(!flag[i]){
rt = i;
break;
}
}
LCA(rt);
memset(count_num, 0, sizeof(count_num));
for(int i=0; i<q; i++){
count_num[answer[i]]++;
}
for(int i=1; i<=n; i++){
if(count_num[i]>0){
//cout<<i<<":"<<count_num[i]<<endl;
printf("%d:%d\n", i, count_num[i]);
}
}
}
return 0;
}

未解决的问题

文章目录
  1. 1. dfs+st在线LCA
    1. 1.1. poj1330
      1. 1.1.1. 题意
      2. 1.1.2. 题解
      3. 1.1.3. ac code
  2. 2. 离线Tarjan算法
    1. 2.1. poj 1470
    2. 2.2. ac code
  3. 3. 未解决的问题
{{ live2d() }}