<题目链接>

题目大意:
给你一棵带有边权的树,然后进行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. Modbus库开发笔记之九:利用协议栈开发Modbus TCP Server应用

    前面我们已经完成了Modbus协议栈的开发,但这不是我们的目的.我们开发它的目的当然是要使用它来解决我们的实际问题.接下来我们就使用刚开发的Modbus协议栈开发一个Modbus TCP Server ...

  2. 高性能MySQL(第3版) 中文PDF带目录清晰版

    下载地址: <高性能MySQL(第3版)>编辑推荐:"只要你不敢以MySQL专家自诩,又岂敢错过这本神书?""一言以蔽之,写得好,编排得好,需要参考时容易到爆 ...

  3. Confluence 6 访问日志脚本

    日志访问脚本在连接:https://confluence.atlassian.com/download/attachments/133267635/Atlassian-accessLogScripts ...

  4. 设置 Confluence 6 日志

    Confluence 使用的是 Apache's log4j 日志服务.能够允许管理员通过编辑配置文件来控制日志的表现和日志输出文件.在系统中有 6 个日志输出级别,请参考 log4j logging ...

  5. flask 中orm关系映射 sqlalchemy的查询

    flask的orm框架(SQLAlchemy)-一对多查询以及多对多查询   一对多,多对多是什么? 一对多.例如,班级与学生,一个班级对应多个学生,或者多个学生对应一个班级. 多对多.例如,学生与课 ...

  6. Java 创建一个窗口,使其启动时位于屏幕中间

    import java.awt.Toolkit; import javax.swing.JFrame; public class WindowInTheMiddle extends JFrame { ...

  7. Java 获取窗口的宽、高

    创建一个新窗口,通过getSize()获取这个窗口的宽.高. import javax.swing.JFrame; public class WindowInTheMiddle extends JFr ...

  8. map reduce程序示例

    map reduce程序示例 package test2; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop. ...

  9. python练习册0006

    第 0006 题:你有一个目录,放了你一个月的日记,都是 txt,为了避免分词的问题,假设内容都是英文,请统计出你认为每篇日记最重要的词. import re import os def get_li ...

  10. java发送http的get、post请求【备忘】

    类 package com.dsideal.kq.Controller; import java.io.BufferedReader; import java.io.IOException; impo ...