<题目链接>

题目大意:
给你一棵带有边权的树,然后进行q次查询,每次查询输出指定两个节点之间的距离。

解题分析:
本题有多重解决方法,首先,可用最短路轻易求解。若只用LCA解决本题,也有三种常用的做法,具体方法如下:

LCA转RMQ解法:

 #include <cstdio>
 #include <cmath>
 #include <vector>
 #include <cstring>
 #include <algorithm>
 using namespace std;

 ;
 int dis[M],dep[M],first[M],pos[M];
 ];
 int cnt;
 struct Edge{
     int to,w;
 };
 vector<Edge>G[M];
 int Min(int a,int b){      //得到深度更小的节点的序号,即更上层的节点
     return dep[a]<dep[b]?a:b;
 }
 void RMQ_init(int n){    //预处理ST表,得到从[i,i+2^j-1]这个区间深度最小的节点的序号,注意,区间的下标代表遍历到的顺序
     ;i<=n;i++)
         st[i][]=i;
     ;(<<j)<=n;j++)
         ;i+(<<j)-<=n;i++)
             st[i][j]=Min(st[i][j-],st[i+(<<(j-))][j-]);
 }
 int RMQ(int l,int r){     //得到指定区间内深度最小的节点的编号,注意这里的l和r指的是按dfs顺序遍历的时间序号
     ))/log(2.0);
     <<k)+][k]);
 }
 int get_LCA(int a,int b){        //在两点遍历顺序的区间内选择深度最小的节点的下标
     return first[a]<first[b]?pos[RMQ(first[a],first[b])]:pos[RMQ(first[b],first[a])];
 }
 void dfs(int u,int fa,int deep){
     dep[cnt]=deep,pos[cnt]=u,first[u]=cnt++;   //表示cnt时间遍历到的节点的深度,和cnt时间遍历到的节点的序号,和第一次遍历到u节点的时间
     ;i<G[u].size();i++){
         int v=G[u][i].to;
         if(v==fa)continue;
         dis[v]=dis[u]+G[u][i].w;  //更新子节点距离
         dfs(v,u,deep+);
         pos[cnt]=u;    //在dfs搜索完u的所有子节点后,回溯到u
         dep[cnt++]=deep;   //u的深度仍然为当前的深度
     }
 }
 int main(){
     int T,n,m;scanf("%d",&T);
     while(T--){
         scanf("%d%d",&n,&m);
         cnt=;
         ;i<M;i++)
             G[i].clear();
         ;i<n;i++){
             int a,b,c;
             scanf("%d%d%d",&a,&b,&c);
             G[a].push_back(Edge{b,c});    //建无向边
             G[b].push_back(Edge{a,c});
         }
         dfs(,-,);
         RMQ_init(*n-);
         while(m--){
             int a,b;
             scanf("%d%d",&a,&b);
             printf(*dis[get_LCA(a,b)]);
         }
     }
     ;
 }

在线ST解法可以看这篇博客 >>>

 #include <cstdio>
 #include <cstring>
 #include <iostream>
 #include <algorithm>
 using namespace std;
 ;
 struct node{
     int to,w,next;
 }e[maxn*];
 ],n,cnt,head[maxn];
 void add(int u,int v,int w){
     e[cnt].to=v,e[cnt].w=w;
     e[cnt].next=head[u],head[u]=cnt++;
 }
 void dfs(int u,int pre,int t){
     dep[u]=t;//深度
     f[u]=pre;//父节点
     ;i=e[i].next){
         int v=e[i].to;
         if(v!=pre){
             dis[v]=dis[u]+e[i].w;//距离跟的距离
             dfs(v,u,t+);
         }
     }
 }
 void init()
 {
     //p[i][j]表示i结点的第2^j祖先
     int i,j;
     ;(<<j)<=n;j++)
         ;i<=n;i++)
             p[i][j]=-;
     ;i<=n;i++)p[i][]=f[i];
     ;(<<j)<=n;j++)
         ;i<=n;i++)
             ]!=-)
                 p[i][j]=p[p[i][j-]][j-];//i的第2^j祖先就是i的第2^(j-1)祖先的第2^(j-1)祖先
 }
 int LCA(int x,int y){
     if(dep[x]<dep[y])swap(x,y);   //令x为深度更深的节点
     int d=dep[x]-dep[y];
     ;(d>>i)!=;i++)
         )x=p[x][i];   //以上的操作是处理较深的节点,使两节点深度一致
     if(x==y)return x;     //如果深度一致时,两节点相同,那么直接返回即可
     ;i>=;i--)
         if(p[x][i]!=p[y][i]){    //跳2^i步不一样,就跳,否则不跳
             x=p[x][i];
             y=p[y][i];
         }
     ];  //上述操作做完,两点的LCA都在上一层,所以再走一步即可
 }
 int main(){
     int T;
     scanf("%d",&T);
     while(T--){
         int m,i,a,b,c,ans;
         scanf("%d%d",&n,&m);
         memset(head,-,sizeof(head));
         cnt=;
         ;i<n;i++){
             scanf("%d%d%d",&a,&b,&c);
             add(a,b,c);
             add(b,a,c);
         }
         dis[]=;
         dfs(,-,);//找到各点的深度和各点的父节点以及距离根的距离
         init();     //初始各个点的2^j祖先是谁
         ;i<m;i++){
             scanf("%d%d",&a,&b);
             ans=dis[a]+dis[b]-*dis[LCA(a,b)];
             printf("%d\n",ans);
         }
     }
     ;
 }

离线Tarjan算法可以看这篇博客 >>> ,还有这篇  >>>

 #include<iostream>
 #include<cstdio>
 #include<cstring>
 #define Maxn 40010
 #define Maxm 210
 using namespace std;

 struct edge{
     int fr,to,w,lca,next;
 }p[Maxn<<],ask[Maxm<<];
 int head[Maxn],ah[Maxn];
 int tot,tot1;
 void addedge(int a,int b,int c){
     p[tot].to=b;
     p[tot].w=c;
     p[tot].next=head[a];
     head[a]=tot++;
 }
 void addedge1(int a,int b){
     ask[tot1].fr=a;
     ask[tot1].to=b;
     ask[tot1].next=ah[a];
     ah[a]=tot1++;
 }
 int fa[Maxn];
 int findset(int x){   //寻找根节点
     return fa[x]==x?x:(fa[x]=findset(fa[x]));
 }
 void unionset(int a,int b){    //将a、b的根节点链接
     fa[findset(a)]=findset(b);
 }
 int vis[Maxn];
 int anc[Maxn];
 int dist[Maxn];
 void LCA(int u,int fa){
     ;i=p[i].next){
         int v=p[i].to;
         if(fa!=v){
             dist[v]=dist[u]+p[i].w;
             LCA(v,u);
             unionset(u,v); //将子树合并到父亲
             anc[findset(u)]=u; //维护新集合指向父亲
         }
     }
     vis[u]=; //设置已访问
     ;i=ask[i].next){ //处理与u关联的边
         int v=ask[i].to;
         if(vis[v]) //若v已访问,则说明u,v的lca是v所在集合的指向
             ask[i].lca=ask[i^].lca=anc[findset(v)];
     }
 }
 void init(int n){
     tot=tot1=;
     memset(head,-,sizeof head);
     memset(ah,-,sizeof ah);
     memset(vis,,sizeof vis);
     ;i<=n;i++) fa[i]=i;
 }
 int main()
 {
     int t,n,m,a,b,c;
     cin>>t;
     while(t--){
         cin>>n>>m;
         init(n);
         ;i<n;i++){
             scanf("%d%d%d",&a,&b,&c);
             addedge(a,b,c);
             addedge(b,a,c);
         }
         ;i<=m;i++){   //把全部的query也建一颗无向树,进行离线处理
             scanf("%d%d",&a,&b);
             addedge1(a,b);
             addedge1(b,a);
         }
         dist[]=;
         LCA(,-);
         ;i<tot1;i+=)
             printf(*dist[ask[i].lca]);
     }
     ;
 }

2018-10-20

HDU 2586 How far away ?(经典)(RMQ + 在线ST+ Tarjan离线) 【LCA】的更多相关文章

  1. HDU 4757 Tree(可持久化Trie+Tarjan离线LCA)

    Tree Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others) Total Su ...

  2. hdu 2586(LCA在线ST)

    How far away ? Time Limit: / MS (Java/Others) Memory Limit: / K (Java/Others) Total Submission(s): A ...

  3. HDU 2586 How far away ?(LCA模板 近期公共祖先啊)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 Problem Description There are n houses in the vi ...

  4. hdu2586(lca模板 / tarjan离线 + RMQ在线)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 题意: 给出一棵 n 个节点的带边权的树, 有 m 个形如 x y 的询问, 要求输出所有 x, ...

  5. HDU 2586 (LCA模板题)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2586 题目大意:在一个无向树上,求一条链权和. 解题思路: 0 | 1 /   \ 2      3 ...

  6. HDU 2586 LCA

    题目大意: 多点形成一棵树,树上边有权值,给出一堆询问,求出每个询问中两个点的距离 这里求两个点的距离可以直接理解为求出两个点到根节点的权值之和,再减去2倍的最近公共祖先到根节点的距离 这是自己第一道 ...

  7. HDU - 2586 How far away ?(LCA模板题)

    HDU - 2586 How far away ? Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & ...

  8. hdu 2586 How far away ?倍增LCA

    hdu 2586 How far away ?倍增LCA 题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2586 思路: 针对询问次数多的时候,采取倍增 ...

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

    HDU 2586 How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/ ...

随机推荐

  1. 步步为营-93-MVC+EF简单实例

    1:创建MVC项目 2:添加EF数据(这里选择DataBaseFirst模式) 3:添加控制器 3.1.1 创建列表页面 3.1.2 html页面 @using MvcApplication1 @{ ...

  2. social psychology 10th David G. Myers

    Social psychology is a science that studies the influences of our situations, with special attention ...

  3. config.GetSection(key)编译不通过

    要安装这个版本

  4. Centos+Redis 集群

    Redis 3.2.6集群搭建 Redis3.0版本之后支持Cluster. 1.1.redis cluster的现状 目前redis支持的cluster特性: 1):节点自动发现 2):slave- ...

  5. 最短路径算法之Dijkstra算法

    参考:<大话数据结构> 这是一个按照路径长度递增的次序产生最短路径的算法.它并不是一次求出源点到目标点的最短路径,而是一步步求出它们之间顶点的最短路径,过程中都是基于已经求出的最短路径的基 ...

  6. Elasticsearch snapshot 备份的使用方法 【备忘】

    常见的数据库都会提供备份的机制,以解决在数据库无法使用的情况下,可以开启新的实例,然后通过备份来恢复数据减少损失.虽然 Elasticsearch 有良好的容灾性,但由于以下原因,其依然需要备份机制. ...

  7. [转] webpack3最新版本配置研究(五) devtool,webpack-dev-server,CommonsChunkPlugin

    devtool devtool是webpack中config自带的属性只要使用就可以了不用安装 webpack官网的解释如下 当 webpack 打包源代码时,可能会很难追踪到错误和警告在源代码中的原 ...

  8. Nginx动静分离架构&&HA-LB集群整合

    Nginx动静分离简单来说就将动态与静态资源分开,不能理解成只是单纯的把动态页面和静态页面物理分离,严格意义上说应该是动态请求跟静态请求分开,可以理解成使用Nginx处理静态页面,Tomcat,Res ...

  9. Codeforces 305E Playing with String 博弈

    我们可以把每段连续可以选的字符看成一个游戏, 那么sg[ i ]表示连续 i 个字符可选的sg值. 然后找找第一个就好啦. #include<bits/stdc++.h> #define ...

  10. Codeforces 1111D Destroy the Colony 退背包 (看题解)

    第一次知道这种背包还能退的.... 我们用dp[ i ]表示选取若干个物品重量到达 i 的方案数. 如果我们g[ i ]表示不用第 x 个物品的, 然后选若干其他的物品到达 i 的方案数. if(i ...