题意:给定一棵树,然后给定若干组询问,问某些点被作为最近公共祖先的次数。
解法:刚刚一直不知道根在哪里给了出来,后来才发现给定的点一定是按照从层数低的节点指向层数高的节点,因此没有双亲的节点为根节点,然后运行tarjan算法即可。这里刚开始的时候我还想把最后的结果除以2输出,这里没有这个必要,因为两个节点一定有一个先后的遍历顺序,这个顺序保证了只被统计一次。
代码如下:
#include <cstdlib> #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; struct Edge { int v, next; }e[2000], q[1000000]; int idx, head[905]; int cnt, link[905]; int set[905]; void insert(int a, int b, int k) { if (k == 1) { e[idx].v = b, e[idx].next = head[a]; head[a] = idx++; } else { q[cnt].v = b, q[cnt].next = link[a]; link[a] = cnt++; } } int find(int x) { return set[x] = x == set[x] ? x : find(set[x]); } int N, tot[905]; char vis[905]; void tarjan(int u) { vis[u] = 1, set[u] = u; for (int i = link[u]; ~i; i = q[i].next) { int v = q[i].v; if (vis[v]) { // printf("%d %d %d\n", u, q[i].v, find(v)); ++tot[find(v)]; } } for (int i = head[u]; ~i; i = e[i].next) { int v = e[i].v; if (!vis[v]) { tarjan(v); set[v] = u; } } } void getint(int &t) { char c; while (c = getchar(), c < '0' || c > '9') ; t = c - '0'; while (c = getchar(), c >= '0' && c <= '9') { t = t * 10 + c - '0'; } } int main() { int x, f, c, Q, rt; while (scanf("%d", &N) != EOF) { cnt = idx = 0; memset(head, 0xff, sizeof (head)); memset(link, 0xff, sizeof (link)); memset(tot, 0, sizeof (tot)); memset(vis, 0, sizeof (vis)); for (int i = 0; i < N; ++i) { scanf("%d:(%d)", &x, &f); for (int j = 0; j < f; ++j) { scanf("%d", &c); vis[c] = 1; insert(x, c, 1), insert(c, x, 1); } } for (int i = 1; i <= N; ++i) { if (!vis[i]) { rt = i; break; } } memset(vis, 0, sizeof (vis)); scanf("%d", &Q); for (int i = 0; i < Q; ++i) { getint(x), getint(c); insert(x, c, 2), insert(c, x, 2); } tarjan(rt); for (int i = 1; i <= N; ++i) { if (tot[i]) printf("%d:%d\n", i, tot[i]); } } return 0; }