LCA问题可以归约为RMQ问题求解。
归约方法为:做一次DFS(Euler Tour),在DFS过程中,记录三个数组的值:
E[i]:在Euler Tour过程中途经的结点编号
L[i]:在Euler Tour过程中途经的结点的层次(从根结点算起的深度)
RMQ问题可以有多种实现方法,本程序采用了Sparse Table方法。
//LCA problem.
#include <iostream>
#include <cmath>
using namespace std;
const int MAXN = 110, MAXNLG = 16;
struct node {
int v; //number of the node
node *next;
};
node *tree[MAXN];
int E[2*MAXN], L[2*MAXN], R[MAXN], visit[MAXN];
int ST[2*MAXN][MAXNLG];
int n, m; //n:number of nodes, m:number of edges
int cnt; //counter for the Euler tour
void init()
{
int i, j, k;
node *p;
scanf("%d%d", &n, &m);
for (i=1; i<=m; i++)
{
scanf("%d%d", &j, &k);
p = new node;
p->v = k;
p->next = tree[j];
tree[j] = p;
p = new node;
p->v = j;
p->next = tree[k];
tree[k] = p;
}
}
void dfs(int i, int depth)
{
node *p;
if (visit[i] == 2) return; //node i has visited over
cnt++;
E[cnt] = i;
if (visit[i]==0)
L[cnt] = depth;
else
L[cnt] = L[R[i]];
//if node i hasn't visited, then it's the first time visit
if (R[i] == -1)
R[i] = cnt;
if (visit[i] ==0)
{
visit[i]++;
//visit each child of node i
for (p = tree[i]; p; p=p->next)
dfs(p->v, depth+1);
visit[i]++;
}
}
void process1()
{
int i;
memset(R, -1, sizeof(R));
dfs(1, 0);
for (i=1; i<=cnt; i++)
printf("%d ", E[i]);
printf("/n");
for (i=1; i<=cnt; i++)
printf("%d ", L[i]);
printf("/n");
for (i=1; i<=n; i++)
printf("%d ", R[i]);
}
void process2(int M[2*MAXN][MAXNLG], int A[2*MAXN], int N)
{
int i, j;
for (i=1; i<=N; i++)
M[i][0] = i;
for (j=1; (1<<j)<=N; j++)
for (i=1; i+(1<<j)-1<=N; i++)
if (A[M[i][j-1]] < A[M[i+(1<<(j-1))][j-1]])
M[i][j] = M[i][j-1];
else
M[i][j] = M[i+(1<<(j-1))][j-1];
}
int rmq(int M[2*MAXN][MAXNLG], int A[2*MAXN], int i, int j)
{
int k;
k = floor(log(j-i+1)/log(2));
if (A[M[i][k]] < A[M[j-(1<<k)+1][k]])
return M[i][k];
else
return M[j-(1<<k)+1][k];
}
int main()
{
int i, j, k, x, y;
init();
process1();
process2(ST, L, cnt);
while (true)
{
scanf("%d%d", &i, &j);
if (i==0) break;
x = R[i]; y = R[j];
if (x > y)
swap(x, y);
k = rmq(ST, L, x, y);
i = E[k];
printf("%d/n", i);
}
return 0;
}
归约方法为:做一次DFS(Euler Tour),在DFS过程中,记录三个数组的值:
E[i]:在Euler Tour过程中途经的结点编号
L[i]:在Euler Tour过程中途经的结点的层次(从根结点算起的深度)
RMQ问题可以有多种实现方法,本程序采用了Sparse Table方法。
//LCA problem.
#include <iostream>
#include <cmath>
using namespace std;
const int MAXN = 110, MAXNLG = 16;
struct node {
int v; //number of the node
node *next;
};
node *tree[MAXN];
int E[2*MAXN], L[2*MAXN], R[MAXN], visit[MAXN];
int ST[2*MAXN][MAXNLG];
int n, m; //n:number of nodes, m:number of edges
int cnt; //counter for the Euler tour
void init()
{
int i, j, k;
node *p;
scanf("%d%d", &n, &m);
for (i=1; i<=m; i++)
{
scanf("%d%d", &j, &k);
p = new node;
p->v = k;
p->next = tree[j];
tree[j] = p;
p = new node;
p->v = j;
p->next = tree[k];
tree[k] = p;
}
}
void dfs(int i, int depth)
{
node *p;
if (visit[i] == 2) return; //node i has visited over
cnt++;
E[cnt] = i;
if (visit[i]==0)
L[cnt] = depth;
else
L[cnt] = L[R[i]];
//if node i hasn't visited, then it's the first time visit
if (R[i] == -1)
R[i] = cnt;
if (visit[i] ==0)
{
visit[i]++;
//visit each child of node i
for (p = tree[i]; p; p=p->next)
dfs(p->v, depth+1);
visit[i]++;
}
}
void process1()
{
int i;
memset(R, -1, sizeof(R));
dfs(1, 0);
for (i=1; i<=cnt; i++)
printf("%d ", E[i]);
printf("/n");
for (i=1; i<=cnt; i++)
printf("%d ", L[i]);
printf("/n");
for (i=1; i<=n; i++)
printf("%d ", R[i]);
}
void process2(int M[2*MAXN][MAXNLG], int A[2*MAXN], int N)
{
int i, j;
for (i=1; i<=N; i++)
M[i][0] = i;
for (j=1; (1<<j)<=N; j++)
for (i=1; i+(1<<j)-1<=N; i++)
if (A[M[i][j-1]] < A[M[i+(1<<(j-1))][j-1]])
M[i][j] = M[i][j-1];
else
M[i][j] = M[i+(1<<(j-1))][j-1];
}
int rmq(int M[2*MAXN][MAXNLG], int A[2*MAXN], int i, int j)
{
int k;
k = floor(log(j-i+1)/log(2));
if (A[M[i][k]] < A[M[j-(1<<k)+1][k]])
return M[i][k];
else
return M[j-(1<<k)+1][k];
}
int main()
{
int i, j, k, x, y;
init();
process1();
process2(ST, L, cnt);
while (true)
{
scanf("%d%d", &i, &j);
if (i==0) break;
x = R[i]; y = R[j];
if (x > y)
swap(x, y);
k = rmq(ST, L, x, y);
i = E[k];
printf("%d/n", i);
}
return 0;
}