树上倍增法求LCA
我们找的是任意两个结点的最近公共祖先, 那么我们可以考虑这么两种种情况:
1.两结点的深度相同.
2.两结点深度不同.
第一步都要转化为情况1,这种可处理的情况。
先不考虑其他, 我们思考这么一个问题: 对于两个深度不同的结点, 把深度更深的那个向其父节点迭代, 直到这个迭代结点和另一个结点深度相同, 那么这两个深度相同的结点的Lca也就是原两个结点的Lca. 因此第二种情况转化成第一种情况来求解Lca是可行的. 这里我们使用倍增法以最快的速度找到相同的深度,然后开始求LCA。求LCA使用倍增法,倍增的条件是找到相同的祖先,减小步距。
/*
* LCA在线算法(倍增法)
*/
const int MAXN = 10010;
const int DEG = 20;
struct Edge
{
int to, next;
} edge[MAXN * 2];
int head[MAXN], tot;
void addedge(int u, int v)
{
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
}
void init()
{
tot = 0;
memset(head, -1, sizeof(head));
}
int fa[MAXN][DEG]; // fa[i][j]表示结点i的第2^j个祖先
int deg[MAXN]; // 深度数组
void BFS(int root)
{
queue<int>que;
deg[root] = 0;
fa[root][0] = root;
que.push(root);
while (!que.empty())
{
int tmp = que.front();
que.pop();
for (int i = 1; i < DEG; i++)
{
fa[tmp][i] = fa[fa[tmp][i - 1]][i - 1];
}
for (int i = head[tmp]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if (v == fa[tmp][0])
{
continue;
}
deg[v] = deg[tmp] + 1;
fa[v][0] = tmp;
que.push(v);
}
}
}
int LCA(int u, int v)
{
if (deg[u] > deg[v])
{
swap(u, v);
}
int hu = deg[u], hv = deg[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 = DEG - 1; i >= 0; i--)
{
if (fa[tu][i] == fa[tv][i])
{
continue;
}
tu = fa[tu][i];
tv = fa[tv][i];
}
return fa[tu][0];
}
bool flag[MAXN];
int main()
{
int T;
int n;
int u, v;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
init();
memset(flag, false, sizeof(flag));
for (int i = 1; i < n; i++)
{
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
flag[v] = true;
}
int root;
for (int i = 1; i <= n; i++)
{
if (!flag[i])
{
root = i;
break;
}
}
BFS(root);
scanf("%d%d", &u, &v);
printf("%d\n", LCA(u, v));
}
return 0;
}