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 }

原文地址:https://www.cnblogs.com/computer-luo/p/9802019.html

时间: 2024-10-12 16:31:44

HDU 2586 How far away ? << LCA转RMQ+ST表 求树上任两点最短距离裸题的相关文章

LCA(最近公共祖先)--tarjan离线算法 hdu 2586

HDU 2586 How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 11320    Accepted Submission(s): 4119 Problem Description There are n houses in the village and some bidirectional roads c

HDU - 2586 - How far away ?

先上题目: How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4936    Accepted Submission(s): 1866 Problem Description There are n houses in the village and some bidirectional roads conne

HDU 2586 How far away?

http://acm.hdu.edu.cn/showproblem.php?pid=2586 题意:给定一个图,有M个询问.每一次询问为询问u,v两个点的距离为多少? 题解:LCA问题. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <cmath> 6 #include <string> 7

HDU 2586 How far away ? (LCA最近公共祖先)

题目地址:HDU 2586 LCA第一发. 纯模板题. 偷懒用的vector,结果一直爆栈.把G++改成C++就过了.. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map> #include <se

HDU 2586 + HDU 4912 最近公共祖先

先给个LCA模板 HDU 1330(LCA模板) #include <cstdio> #include <cstring> #define N 40005 struct Edge{ int x,y,d,ne; }; Edge e[N*2],e2[N*2]; int be[N],be2[N],all,all2,n,m; bool vis[N]; int fa[N]; int ancestor[N][3]; int dis[N]; void add(int x, int y, int

hdu 2586 LCA模板题(离线算法)

http://acm.hdu.edu.cn/showproblem.php?pid=2586 Problem Description There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B&quo

最近公共祖先(lca) hdu 2586

hdu 2586 How far away ? 题目大意:给定n-1条边构成一棵树,无向的:和m个询问,对于每一个询问按顺序回答. 结题思路:lca算法算出最近公共祖先,然后dis[u]+dis[v]-2*dis[father](father是u,v的最近公共祖先),小trick是在构造询问树的时候把权值设成询问对应的输入顺序 #include <iostream> #include <cstdio> #include <cstring> #include <al

HDU 2586 LCA-Tarjan

还是LCA-tarjan算法,跟POJ 1330做法基本类似,只是这个题目要求输出两个点的最短距离,其实利用LCA的性质,就是 两个点分别到最近公共祖先的距离之和 一开始本来想用并查集把路径长度给找出来,但是不太好处理,原因是我刚好找到的这个点还没有加入到并查集中,(因为还没回溯上去),如果马上就合并,我还得把父亲传下来,还破坏了原有算法的结构(在孩子这里就进行了并查集合并操作),会产生奇怪的结果... 有个很简便又机智的方法,就是在dfs的过程中 一边记录从rt到下面的点的距离dis,假设 A

hdu 2586 How far away ?倍增LCA

hdu 2586 How far away ?倍增LCA 题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2586 思路: 针对询问次数多的时候,采取倍增求取LCA,同时跟新距离数组 因为 \(2^{16} > 40000\) 所以所以表示祖先的数组dp[][]第二维取到16即可 就这道题来说,与比较tarjan比较,稍快一点 代码: #include <iostream> #include <algorithm> #includ

HDU 2586 LCA模板题

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2586 题目大意:在一个无向树上,求一条链权和. 解题思路: 0 | 1 /   \ 2      3 设dist[i]为i到根0的链和,求法(Dfs过程中dist[v]=dist[u]+e[i].w) 对于树中任意两点形成的链,可以通过LCA最近公共祖先剖分. 比如2->3,就可以经过LCA点1:  2->1->3 链和=dist[u]+dist[v]-2*dist[LCA[u,v]] (