Lca:求树上任意两个节点的最近公共祖先
Rmq:区间最值查询
(2)
/ \
(3) (4)
(5)
一个nlogn 预处理,O(1)查询的算法.
Step 1:
Step 2:
Step 3:
hdu4547:http://acm.hdu.edu.cn/showproblem.php?pid=4547
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#include <map>
using namespace std;
const int MAXN = 100000 + 1;
const int char_len = 40 + 1;
int n, m, tot, root, step;
struct Edge
{
int v, next;
}edge[MAXN * 2];
int e, head[MAXN], indegree[MAXN], deep[MAXN * 2], pos[MAXN], d[MAXN * 2][20];
char stra[char_len], strb[char_len];
map <string, int> mt;
void init()
{
e = tot = step = 0;
mt.clear();
memset(head, -1, sizeof(head));
memset(pos, -1, sizeof(pos));
memset(indegree, 0, sizeof(indegree));
}
void add(int u, int v)
{
edge[e].v = v;
edge[e].next = head[u];
head[u] = e++;
}
void Lca_to_Rmq(int p, int dfn)
{
if (pos[p] == -1) pos[p] = step;
deep[step++] = dfn;
for (int i = head[p]; i != -1; i = edge[i].next)
{
Lca_to_Rmq(edge[i].v, dfn + 1);
deep[step++] = dfn;
}
}
void Init_Rmq()
{
for (int i = 0; i < 2 * n - 1; i++) d[i][0] = deep[i];
for (int j = 1; (1 << j) <= 2 * n - 1; j++)
for (int i = 0; i + (1 << j) - 1 < 2 * n - 1; i++)
d[i][j] = min(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]);
}
int Rmq(int l, int r)
{
int k = 0;
if (l > r)
{
int temp = l;
l = r;
r = temp;
}
while ((1 << (k + 1)) <= r - l + 1) k++;
return min(d[l][k], d[r - (1 << k) + 1][k]);
}
void input()
{
int t, u, v;
scanf("%d", &t);
while (t--)
{
init();
scanf("%d %d", &n, &m);
for (int i = 0; i < n - 1; i++)
{
scanf("%s %s", stra, strb);
if (!mt[stra]) mt[stra] = ++tot;
if (!mt[strb]) mt[strb] = ++tot;
u = mt[stra], v = mt[strb];
indegree[u]++;
add(v, u);
}
for (int i = 1; i <= n; i++)
{
if (indegree[i] == 0)
{
root = i;
break;
}
}
Lca_to_Rmq(root, 0);
Init_Rmq();
for (int i = 0; i < m; i++)
{
scanf("%s %s", stra, strb);
u = mt[stra], v = mt[strb];
int f = Rmq(pos[u], pos[v]);
if (n == 1)
{
printf("0\n");
continue;
}
printf("%d\n", deep[pos[u]] - f + (deep[pos[v]] == f ? 0 : 1));
}
}
}
int main()
{
input();
return 0;
}