题目链接:http://poj.org/problem?id=1470。
思路:给出一棵树和多个求两个节点的最近公共祖先的查询,使用tarjan 离线算法应对多对查询,使用并查集来合并顶点,不过这里不能够使用按秩合并,只能将root的所有孩子节点并到root上,代码如下:
#include <iostream>
#include <vector>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 905;
vector<int> graph[maxn], query[maxn];
int pre[maxn], visit[maxn], cnt[maxn], in[maxn];
int n, m;
void makeset(int root) {
pre[root] = root;
}
int find(int root) {
return (pre[root] == root) ? root : (pre[root] = find(pre[root]));
}
void Union(int s, int v) {
int s1 = find(s);
int v1 = find(v);
pre[v1] = s1;
}
void tarjan(int root) {
makeset(root);
for (int i = 0; i < graph[root].size(); i++) {
int v = graph[root][i];
tarjan(v);
Union(root, v);
}
visit[root] = 1;
for (int i = 0; i < query[root].size(); i++) {
int v = query[root][i];
if (visit[v]) cnt[find(v)]++;
}
}
int main() {
int node, succ, num, s, e, root;
while (cin >> n) {
//init
memset(pre, 0, sizeof(pre));
memset(visit, 0, sizeof(visit));
memset(cnt, 0, sizeof(cnt));
memset(in, 0, sizeof(in));
for (int i = 1; i <= n; i++) {
graph[i].clear();
query[i].clear();
}
for (int i = 0; i < n; i++) {
scanf("%d:(%d)", &node, &num);
for (int j = 0; j < num; j++) {
cin >> succ;
graph[node].push_back(succ);
in[succ]++;
}
}
cin >> m;
for (int i = 0; i < m; i++) {
while (getchar() != '(');
cin >> s >> e;
query[s].push_back(e);
query[e].push_back(s);
while (getchar() != ')');
}
for (int i = 1; i <= n; i++) {//the first input is not always the root node
if (!in[i]) {
root = i;
break;
}
}
tarjan(root);
for (int i = 1; i <= n; i++) {
if (cnt[i]) printf("%d:%d\n", i, cnt[i]);
}
}
return 0;
}