HDU 2586 How far away ? << LCA转RMQ+ST表 求树上任两点最短距离裸题
此题还有LCA+tarjin离线查询做法,详见这里
关于ST表
解决RMQ问题,dp[i][j]表示从第i位开始长度为(1<<j)的区间的最值
维护的时候采用倍增思想,维护dp[i][j+1]=opt(dp[i][j],dp[i+(1<<j)][j])
查询的时候,两端点为l,r,则长度len=r-l,寻找一个k,使(1<<k)大于等于len/2,这样就使得 [l,l+(1<<k)]和[r-(1<<k),r]覆盖整个区间
然后此时,就可以直接用st表查了,ans=opt(dp[l][k],dp[r-(1<<k)][k])
关于LCA转RMQ
用到三个数组
dfsn[i] 表示dfs序,表示第i个访问的节点,此处需要注意的是回溯时也要记录
dist[i] 表示深度,节点i的深度
st[i] 表示节点i第一次出现的位置
然后在dfsn上做RMQ,用dist作为排序规则
这样即是在两节点之间寻找一个深度最小的点,那么这个点就是他们的LCA了
此题代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=4e4+7; 4 struct Edge{ 5 int to,len; 6 }; 7 8 vector<Edge> G[maxn]; 9 int n; 10 int dfsn[maxn*4]; 11 int dist[maxn]; 12 int st[maxn]; 13 int tot; 14 int dp[maxn*4][20]; 15 void init() 16 { 17 for(int i=0;i<=n;i++) 18 G[i].clear(); 19 memset(dfsn,0,sizeof(dfsn)); 20 memset(dist,0,sizeof(dist)); 21 tot=0; 22 } 23 void dfs(int u,int fa) 24 { 25 st[u]=++tot; 26 dfsn[tot]=u; 27 for(int i=0;i<G[u].size();i++) 28 { 29 Edge &v=G[u][i]; 30 if(v.to==fa) continue; 31 dist[v.to]=dist[u]+v.len; 32 dfs(v.to,u); 33 dfsn[++tot]=u; 34 } 35 } 36 void rmq() 37 { 38 for(int i=1;i<=tot;i++) 39 dp[i][0]=dfsn[i]; 40 for(int j=0;(1<<j)<=tot;j++) 41 { 42 for(int i=1;i+(1<<j)<=tot;i++) 43 { 44 if(dist[dp[i][j]]<dist[dp[i+(1<<j)][j]]) 45 dp[i][j+1]=dp[i][j]; 46 else dp[i][j+1]=dp[i+(1<<j)][j]; 47 } 48 } 49 } 50 int query(int u,int v) 51 { 52 int ss=u,tt=v; 53 u=st[u],v=st[v]; 54 if(u>v) swap(u,v); 55 int len=v-u; 56 int k=0; 57 while((1<<k)<len/2) 58 k++; 59 int LCA; 60 if(dist[dp[u][k]]<dist[dp[v-(1<<k)][k]]) 61 LCA=dp[u][k]; 62 else LCA=dp[v-(1<<k)][k]; 63 return dist[ss]+dist[tt]-2*dist[LCA]; 64 } 65 int main() 66 { 67 int T; 68 scanf("%d",&T); 69 while(T--) 70 { 71 int q; 72 scanf("%d%d",&n,&q); 73 init(); 74 for(int i=1,u,v,len;i<n;i++) 75 { 76 scanf("%d%d%d",&u,&v,&len); 77 G[u].push_back(Edge {v,len}); 78 G[v].push_back(Edge {u,len}); 79 } 80 dfs(1,-1); 81 rmq(); 82 while(q--) 83 { 84 int u,v; 85 scanf("%d%d",&u,&v); 86 printf("%d\n",query(u,v)); 87 } 88 } 89 }