这次使用离线算法来解决最近公共祖先的问题。
离线算法可以一遍 dfs 处理完所有的查询,因而需要把查询全部储存起来。
具体的 dfs 过程是:
所有节点最初标记为白色,第一次经过该节点时,将其染成灰色,第二次经过该节点时(即离开该节点时)将其染成黑色。
在 dfs 的某个状态下,白色代表未访问的节点,黑色代表已经访问完该节点为根整个子树,灰色代表正在访问该节点为根的子树。
可以在每个节点处查询与该节点相关的所有查询,每个查询对应有另一个节点X(可能是自身):
如果X是白色,说明还未访问到,信息不全,以后再处理;
如果X是灰色,说明当前节点是X子树中的元素,那么它们的最近公共祖先就是X;
如果X是黑色,可以寻找X的一个最近的灰色祖先节点Y,当前节点是Y的子树中的元素,X和当前节点的最近公共祖先就是Y。
如何查询与特定节点相关的所有查询呢?可以使用vector存下来。
如何寻找X的一个最近的灰色祖先节点Y呢?可以用并查集。
使用这种方法,每个查询实际上会被处理一次(白、黑/灰)或两次(灰、黑)。复杂度是 O(m) (m为查询量)的。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define MAX 100005
#define MAX_HASH 200005
using namespace std;
char names[MAX_HASH][50];
vector<int> sons[MAX_HASH];
int root[MAX_HASH];
int color[MAX_HASH];
int query[MAX][2];
vector<int> i_query[MAX_HASH];
int ans[MAX];
int n;
unsigned int BKDRHash(char *str)
{
unsigned int seed = 131;
unsigned int hash = 0;
while (*str)
{
hash = hash * seed + (*str++);
}
return (hash & 0x7FFFFFFF) % MAX_HASH;
}
int get_index(char *str) {
int res = BKDRHash(str);
while (strlen(names[res]) != 0 && strcmp(str, names[res])) {
res++;
if (res > MAX_HASH) res -= MAX_HASH;
}
if (strlen(names[res]) == 0) {
strcpy(names[res], str);
}
return res;
}
int get_root(int i) {
return color[i] == 1 ? i : root[i] = get_root(root[i]);
}
void dfs(int root_idx) {
color[root_idx] = 1;
int len = i_query[root_idx].size();
for (int i = 0; i < len; i++) {
int q_idx = i_query[root_idx][i];
int another = root_idx == query[q_idx][0] ? query[q_idx][1] : query[q_idx][0];
if (color[another] == 1) {
ans[q_idx] = another;
}
else if (color[another] == 2 && ans[q_idx] == 0) {
ans[q_idx] = get_root(another);
}
}
int son_len = sons[root_idx].size();
for (int i = 0; i < son_len; i++) {
dfs(sons[root_idx][i]);
}
color[root_idx] = 2;
}
int main() {
char s1[50], s2[50];
int i1, i2;
int start;
cin >> n;
cin >> s1 >> s2;
i1 = get_index(s1);
i2 = get_index(s2);
sons[i1].push_back(i2);
root[i2] = i1;
start = i1;
while (--n) {
cin >> s1 >> s2;
i1 = get_index(s1);
i2 = get_index(s2);
sons[i1].push_back(i2);
root[i2] = i1;
}
cin >> n;
for (int i = 0; i < n; i++) {
cin >> s1 >> s2;
i1 = get_index(s1);
i2 = get_index(s2);
query[i][0] = i1;
query[i][1] = i2;
i_query[i1].push_back(i);
i_query[i2].push_back(i);
}
dfs(start);
for (int i = 0; i < n; i++) {
cout << names[ans[i]] << endl;
}
return 0;
}