最近公共祖先:在线算法和离线算法。
离线算法(Tarjan):
伪代码如下:
注意:
存储树的时候存储的是单向边,所以checked[u] = true 写在中间不会出现死循环, 这样可以避免重复计算公共祖先。
如果存储的树是双向边的话,就必须写在前面,这样子可以避免死循环,双向边的情况如下
但是这道问题确实统计公共祖先的次数的,因此如果将checked[u] = true 放到前面的话,可能会重复计数。随意用单向边放到中间。
完全解决这个纠结的小问题的方法是用两个不同的标记数组标记访问次序。此处略去……
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int MAXN = 1024;
vector<int> edge[MAXN];
int ques[MAXN][MAXN], fa[MAXN], cnt[MAXN], ind[MAXN];
bool vis[MAXN];
int n, m;
int father(int x) {
if (x == fa[x]) return x;
return x = father(fa[x]);
}
void dfs(int x) {
fa[x] = x;
for (int i=0; i<edge[x].size(); i++) {
dfs(edge[x][i]);
fa[edge[x][i]] = x;
}
vis[x] = true;
for (int i=1; i<=n; i++) {
if (vis[i] && ques[x][i])
cnt[father(i)] += ques[x][i];
}
}
int main() {
while (scanf("%d", &n) == 1) {
for (int i=1; i<=n; i++) {
edge[i].clear();
}
memset(ques, 0, sizeof(ques));
memset(vis, false, sizeof(vis));
memset(cnt, 0, sizeof(cnt));
memset(ind, 0, sizeof(ind));
int a, b;
for (int i=0; i<n; i++) {
scanf("%d:(%d)", &a, &m);
for (int j=0; j<m; j++) {
scanf(" %d", &b);
edge[a].push_back(b);
ind[b]++;
}
}
scanf(" %d", &m);
for (int i=0; i<m; i++) {
scanf(" (%d %d)", &a, &b);
ques[a][b]++;
ques[b][a]++;
}
for (int i=1; i<=n; i++)
if (!ind[i]) {
dfs(i); break;
}
for (int i=1; i<=n; i++)
if (cnt[i]) printf("%d:%d\n", i, cnt[i]);
}
return 0;
}