题目大意:
多点形成一棵树,树上边有权值,给出一堆询问,求出每个询问中两个点的距离
这里求两个点的距离可以直接理解为求出两个点到根节点的权值之和,再减去2倍的最近公共祖先到根节点的距离
这是自己第一道lca题目
学习了两种方法
第一种在ST算法,利用RMQ思想预处理
1 /*在线ST算法*/ 2 #pragma comment(linker, "/STACK:102400000,102400000") 3 #include <cstdio> 4 #include <iostream> 5 #include <cstring> 6 #include <algorithm> 7 using namespace std; 8 #define N 40010 9 10 int first[N] , k; 11 12 struct Edge{ 13 int x , y , w , next; 14 Edge(){} 15 Edge(int x , int y , int w , int next):x(x),y(y),w(w),next(next){} 16 }e[N<<1]; 17 18 void add_edge(int x , int y , int w) 19 { 20 e[k] = Edge(x , y , w , first[x]); 21 first[x] = k++; 22 } 23 int dp[N<<1][28]; 24 int id[N<<1] , dep[N<<1] , dis[N] , No[N] , dfs_clock; 25 void dfs(int u , int f , int d) 26 { 27 id[++dfs_clock] = u , No[u] = dfs_clock , dep[dfs_clock] = d; 28 for(int i=first[u] ; ~i ; i=e[i].next){ 29 int v = e[i].y; 30 if(v == f) continue; 31 dis[v] = dis[u]+e[i].w; 32 dfs(v , u , d+1); 33 id[++dfs_clock] = u , dep[dfs_clock] = d; 34 } 35 } 36 37 void ST(int n) 38 { 39 for(int i=1 ; i<=n ; i++) dp[i][0] = i; 40 for(int j=1 ; (1<<j)<=n ; j++){ 41 for(int i=1 ; i+(1<<j)-1<=n ; i++){ 42 int a = dp[i][j-1] , b = dp[i+(1<<(j-1))][j-1]; 43 dp[i][j] = dep[a]<dep[b]?a:b; 44 } 45 } 46 } 47 48 int RMQ(int l , int r) 49 { 50 int k=0; 51 while((1<<(k+1))<=r-l+1) k++; 52 int a = dp[l][k] , b = dp[r-(1<<k)+1][k]; 53 return dep[a]<dep[b]?a:b; 54 } 55 56 int LCA(int u , int v) 57 { 58 int x = No[u] , y = No[v]; 59 if(x>y) swap(x , y); 60 return id[RMQ(x , y)]; 61 } 62 63 int n , m; 64 65 int main() 66 { 67 // freopen("in.txt" , "r" , stdin); 68 int T , x , y , w; 69 scanf("%d" , &T); 70 while(T--) 71 { 72 scanf("%d%d" , &n , &m); 73 memset(first , -1 , sizeof(first)); 74 k = 0; 75 for(int i=1 ; i<n ; i++){ 76 scanf("%d%d%d" , &x , &y , &w); 77 add_edge(x , y , w); 78 add_edge(y , x , w); 79 } 80 dfs_clock = 0; 81 dis[1] = 0; 82 dfs(1 , 0 , 1); 83 ST(2*n-1); 84 while(m--){ 85 scanf("%d%d" , &x , &y); 86 int anc = LCA(x , y); 87 printf("%d " , dis[x]-2*dis[anc]+dis[y]); 88 } 89 } 90 return 0; 91 }
第二种是离线的Tarjan算法,利用并查集的思想解决
1 /*在线ST算法*/ 2 #pragma comment(linker, "/STACK:102400000,102400000") 3 #include <cstdio> 4 #include <iostream> 5 #include <cstring> 6 #include <algorithm> 7 using namespace std; 8 #define N 40010 9 #define M 205 10 int first[N] , k; 11 12 struct Edge{ 13 int x , y , w , next; 14 Edge(){} 15 Edge(int x , int y , int w , int next):x(x),y(y),w(w),next(next){} 16 }e[N<<1]; 17 18 int _first[N] , _k; 19 20 struct Que{ 21 int v , lca , next; 22 Que(){} 23 Que(int v , int lca , int next):v(v),lca(lca),next(next){} 24 }eq[M<<1]; 25 26 void add_edge(int x , int y , int w) 27 { 28 e[k] = Edge(x , y , w , first[x]); 29 first[x] = k++; 30 } 31 32 void add_que_edge(int x , int y) 33 { 34 eq[_k] = Que(y , 0 , _first[x]); 35 _first[x] = _k++; 36 } 37 38 int fa[N] , dis[N]; 39 bool vis[N]; 40 int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} 41 42 void tarjan(int u) 43 { 44 vis[u] = true , fa[u] = u; 45 for(int i=first[u] ; ~i ; i=e[i].next){ 46 int v = e[i].y; 47 if(vis[v]) continue; 48 dis[v] = dis[u]+e[i].w; 49 tarjan(v); 50 fa[v] = u; 51 } 52 for(int i=_first[u] ; ~i ; i=eq[i].next){ 53 int v = eq[i].v; 54 if(vis[v]) eq[i].lca = eq[i^1].lca = find(eq[i].v); 55 } 56 } 57 int n , m; 58 59 int main() 60 { 61 // freopen("in.txt" , "r" , stdin); 62 int T , x , y , w; 63 scanf("%d" , &T); 64 while(T--) 65 { 66 scanf("%d%d" , &n , &m); 67 memset(first , -1 , sizeof(first)); 68 k = 0; 69 for(int i=1 ; i<n ; i++){ 70 scanf("%d%d%d" , &x , &y , &w); 71 add_edge(x , y , w); 72 add_edge(y , x , w); 73 } 74 memset(_first , -1 , sizeof(_first)); 75 _k = 0; 76 for(int i=0 ; i<m ; i++){ 77 scanf("%d%d" , &x , &y); 78 add_que_edge(x , y); 79 add_que_edge(y , x); 80 } 81 memset(vis , 0 , sizeof(vis)); 82 tarjan(1); 83 for(int i=0 ; i<m*2 ; i+=2){ 84 int lca = eq[i].lca; 85 printf("%d " , dis[eq[i].v]-2*dis[lca]+dis[eq[i^1].v]); 86 } 87 } 88 return 0; 89 }