记节点v到根点的深度为depth[v],那么当w是v和u的最近公共祖先时,可以想让v和u在同一个深度,即让深度高的走|depth[v]-depth[u]|然后一起走,
直到遇见相同的祖先时就是最近公共祖先了。如果是计算一次的话还可以,但有很多询问时就不行了。
那么可以利用父亲节点的信息来做了,可以通过parent2[v] = parent[parent[v]]得到v向上走两步的节点。在利用这个信息,
又可以通过parent4[v] = parent2[parent2[v]]得到向上走4步得到的节点,这样搜索的复杂度是O(long(n))了,预处理parent[k][v]的复杂度是O(nlong(n))。
1 #include <iostream> 2 #include <stdio.h> 3 #include <vector> 4 #include <string.h> 5 using namespace std; 6 const int N = 1e4+10; 7 vector<int> G[N]; 8 int vis[N], parent[30][N], depth[N], t, n; 9 10 void dfs(int v, int p, int d){ 11 parent[0][v] = p; 12 depth[v] = d; 13 for(int i = 0; i < G[v].size(); i ++){ 14 if(G[v][i] != p) dfs(G[v][i],v,d+1); 15 } 16 } 17 int lca(int u, int v){ 18 if(depth[u] > depth[v]) swap(u,v); 19 for(int k = 0; k < 30; k ++){ 20 if((depth[v]-depth[u]) >> k&1){ 21 v = parent[k][v]; 22 } 23 } 24 if(v == u) return u; 25 for(int k = 29; k >= 0; k--){ 26 if(parent[k][u] != parent[k][v]){ 27 u = parent[k][u]; 28 v = parent[k][v]; 29 } 30 } 31 return parent[0][u]; 32 } 33 void init(){ 34 memset(parent,-1,sizeof(parent)); 35 for(int i = 1; i < N; i ++){ 36 parent[0][i] = i; 37 G[i].clear(); 38 } 39 memset(vis,0,sizeof(vis)); 40 memset(depth,0,sizeof(depth)); 41 } 42 int main(){ 43 scanf("%d",&t); 44 while(t--){ 45 scanf("%d",&n); 46 init(); 47 int x, y; 48 for(int i = 1; i < n; i ++){ 49 scanf("%d %d",&x,&y); 50 G[x].push_back(y); 51 G[y].push_back(x); 52 vis[y] = 1; 53 } 54 for(int i = 1; i <= n; i ++){ 55 if(!vis[i]){ 56 dfs(i,-1,0); 57 for(int k = 0; k+1 < 30; k ++){ 58 for(int v = 0; v < N; v ++){ 59 if(parent[k][v] < 0) parent[k+1][v] = -1; 60 else parent[k+1][v] = parent[k][parent[k][v]]; 61 } 62 } 63 } 64 } 65 scanf("%d%d",&x,&y); 66 printf("%d\n",lca(x,y)); 67 } 68 return 0; 69 }
时间: 2024-11-09 00:03:02