其实本质是就是一种二分。
首先我们先对这个树DFS一遍,记录每个节点的深度(depth),并且记录他的第2^j个祖先。
当我们找u和v的LCA时,首先把他们的深度变成一样的,然后如果pre[u][k] == pre[v][k],那么当前是公共祖先,但不一定是最近的,所以k--;
如果不符合就u = pre[u][k] v = pre[v][k],接着找。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
struct node{
int v,next;
}edge[111111];
int pre[999][111];
int head[1111];
int depth[1111];
int ans[1111];
int n;
int e;
int root[1111];
void addedge(int u,int v){
edge[e].v = v;
edge[e].next = head[u];
head[u] = e++;
}
void dfs(int k){
for(int i = head[k] ; i != -1 ; i = edge[i].next){
int v = edge[i].v;
pre[v][0] = k;
int m = k;
depth[v] = depth[k] + 1;
for(int j = 0 ; pre[m][j] != 0 ; j ++){
pre[v][j+1] = pre[m][j];
m = pre[m][j];
}
dfs(v);
}
}
int LCA(int u,int v){
if(u == v)
return u;
if(depth[u] < depth[v]){
swap(u,v);
}
int m = depth[u] - depth[v];
int k = 0;
while(m){
if(m%2 == 1){
u = pre[u][k];
}
m /= 2;
k++;
}
if(u == v){
return u;
}
k = 0;
while(u != v){
if(pre[u][k] != pre[v][k] || (pre[u][k] == pre[v][k] && k == 0)){
u = pre[u][k];
v = pre[v][k];
k++;
}
else{
k--;
}
}
return u;
}
void init(){
int e = 0;
memset(head,-1,sizeof(head));
memset(depth,0,sizeof(depth));
memset(pre,0,sizeof(pre));
memset(ans,0,sizeof(ans));
for(int i = 1 ; i <= n ; i ++){
root[i] = i;
}
}
int main()
{
while(scanf("%d",&n) != EOF){
init();
for(int i = 1 ; i <= n ; i ++){
int id,num;
scanf("%d:",&id);
scanf("(%d)",&num);
for(int j = 0 ; j < num ; j ++){
int v;
scanf("%d",&v);
root[v] = id;
addedge(id,v);
}
}
int r = 1;
while(root[r] != r){
r = root[r];
}
dfs(r);
int q;
scanf("%d",&q);
while(q--){
char c1[2],c2[2];
int u,v;
scanf("%1s%d%d%1s",c1,&u,&v,c2);
ans[LCA(u,v)]++;
}
for(int i = 1 ; i <= n ; i ++){
if(ans[i] != 0){
printf("%d:%d\n",i,ans[i]);
}
}
}
return 0;
}