题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586

题意: 给出一棵 n 个节点的带边权的树, 有 m 个形如 x y 的询问, 要求输出所有 x, y节点之间的最短距离.

思路: dis[i] 存储 i 节点到根节点的最短距离, lca 为 x, y 的最近公共祖先, 那么 x, y 之间的最短距离为: dis[x] + dis[y] - 2 * dis[lca] .

解法1: tarjan离线算法

关于该算法

 Tarjan(u)//marge和find为并查集合并函数和查找函数
{
for each(u,v) //访问所有u子节点v
{
Tarjan(v); //继续往下遍历
marge(u,v); //合并v到u上
标记v被访问过;
}
for each(u,e) //访问所有和u有询问关系的e
{
如果e被访问过;
u,e的最近公共祖先为find(e);
}
}

详解见: http://www.cnblogs.com/ECJTUACM-873284962/p/6613379.html

代码:

 #include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std; const int MAXN = 4e4 + ;
struct node{
int u, v, w, lca, next;
}edge1[MAXN << ], edge2[];//edge1记录树, edge2记录询问 int vis[MAXN], pre[MAXN], dis[MAXN];//vis[i]标记i是否已经搜索过, pre[i]记录i的根节点, dis[i]记录i到根节点的距离
int head1[MAXN], head2[MAXN], ance[MAXN], ip1, ip2; void init(void){
memset(vis, , sizeof(vis));
memset(dis, , sizeof(dis));
memset(head1, -, sizeof(head1));
memset(head2, -, sizeof(head2));
ip1 = ip2 = ;
} void addedge1(int u, int v, int w){//前向星
edge1[ip1].v = v;
edge1[ip1].w = w;
edge1[ip1].next = head1[u];
head1[u] = ip1++;
} void addedge2(int u, int v){
edge2[ip2].u = u;
edge2[ip2].v = v;
edge2[ip2].lca = -;
edge2[ip2].next = head2[u];
head2[u] = ip2++;
} int find(int x){
return pre[x] == x ? x : pre[x] = find(pre[x]);
} void jion(int x, int y){
x = find(x);
y = find(y);
if(x != y) pre[y] = x;
} void tarjan(int u){
vis[u] = ;
ance[u] = pre[u] = u;
for(int i = head1[u]; i != -; i = edge1[i].next){
int v = edge1[i].v;
int w = edge1[i].w;
if(!vis[v]){
dis[v] = dis[u] + w;
tarjan(v);
jion(u, v);
}
}
for(int i = head2[u]; i != -; i = edge2[i].next){
int v = edge2[i].v;
if(vis[v]) edge2[i].lca = edge2[i ^ ].lca = ance[find(v)];
}
} int main(void){
int t, n, m, x, y, z;
scanf("%d", &t);
while(t--){
init();
scanf("%d%d", &n, &m);
for(int i = ; i < n; i++){
scanf("%d%d%d", &x, &y, &z);
addedge1(x, y, z);
addedge1(y, x, z);
}
for(int i = ; i < m; i++){
scanf("%d%d", &x, &y);
addedge2(x, y);
addedge2(y, x);
}
dis[] = ;
tarjan();
for(int i = ; i < m; i++){
int cc = i << ;
int u = edge2[cc].u;
int v = edge2[cc].v;
int lca = edge2[cc].lca;
printf("%d\n", dis[u] + dis[v] - * dis[lca]);
}
}
return ;
}

解法2: lca转RMQ

关于该算法

ver[] 存储树的 dfs 路径

first[u] 为顶点 u 在 ver 数组中第一次出现时的下标

deep[indx] 为顶点 ver[indx] 的深度

对于求 x, y 的 lca, 先令 l = first[x], r = first[y], 即 l, r 分别为 x, y 第一次在 ver 数组中出现时对应的下标

在 deep[] 数组中找到区间 [l, r] 中的最小值, 其下标对应的 ver 值即为 x, y 的 lca. (区间最值可以用 RMQ 处理)

详解见: http://blog.csdn.net/u013076044/article/details/41870751

代码:

 #include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
using namespace std; const int MAXN = 4e4 + ;
struct node{
int v, w, next;
}edge[MAXN << ]; int dp[MAXN << ][]; //dp[i][j]存储deep数组中下标i开始长度为2^j的子串中最小值的下标
int first[MAXN], ver[MAXN << ], deep[MAXN << ];
int vis[MAXN], head[MAXN], dis[MAXN], ip, indx; inline void init(void){
memset(vis, , sizeof(vis));
memset(head, -, sizeof(head));
ip = ;
indx = ;
} void addedge(int u, int v, int w){
edge[ip].v = v;
edge[ip].w = w;
edge[ip].next = head[u];
head[u] = ip++;
} void dfs(int u, int h){
vis[u] = ; //标记已搜索过的点
ver[++indx] = u; //记录dfs路径
first[u] = indx; //记录顶点u第一次出现时对应的ver数组的下标
deep[indx] = h; //记录ver数组中对应下标的点的深度
for(int i = head[u]; i != -; i = edge[i].next){
int v = edge[i].v;
if(!vis[v]){
dis[v] = dis[u] + edge[i].w;
dfs(v, h + );
ver[++indx] = u;
deep[indx] = h;
}
}
} void ST(int n){
for(int i = ; i <= n; i++){
dp[i][] = i;
}
for(int j = ; ( << j) <= n; j++){
for(int i = ; i + ( << j) - <= n; i++){
int x = dp[i][j - ], y = dp[i + ( << (j - ))][j - ];
dp[i][j] = deep[x] < deep[y] ? x : y;
}
}
} int RMQ(int l, int r){
int len = log2(r - l + );
int x = dp[l][len], y = dp[r - ( << len) + ][len];
return deep[x] < deep[y] ? x : y;
} int LCA(int x, int y){
int l = first[x], r = first[y];
if(l > r) swap(l, r);
int pos = RMQ(l, r);
return ver[pos];
} int main(void){
int t, n, m, x, y, z;
scanf("%d", &t);
while(t--){
init();
scanf("%d%d", &n, &m);
for(int i = ; i < n; i++){
scanf("%d%d%d", &x, &y, &z);
addedge(x, y, z);
addedge(y, x, z);
}
dis[] = ;
dfs(, );
ST( * n - );
for(int i = ; i < m; i++){
scanf("%d%d", &x, &y);
int lca = LCA(x, y);
printf("%d\n", dis[x] + dis[y] - * dis[lca]);
}
}
return ;
}

hdu2586(lca模板 / tarjan离线 + RMQ在线)的更多相关文章

  1. hdu2874(lca / tarjan离线 + RMQ在线)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2874 题意: 给出 n 个顶点 m 条边的一个森林, 有 k 个形如 x y 的询问, 输出 x, ...

  2. SPOJ 10628 Count on a tree(Tarjan离线 | RMQ-ST在线求LCA+主席树求树上第K小)

    COT - Count on a tree #tree You are given a tree with N nodes.The tree nodes are numbered from 1 to  ...

  3. hdu 2586 How far away?(LCA模板题+离线tarjan算法)

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

  4. 洛谷 P3379 【模板】最近公共祖先(LCA)Tarjan离线

    题目链接:LCA tarjan离线 这道题目WA无数发,最后还是参考了大神的blog 谁会想到因为一个输入外挂WA呢 大概是我的挂是假挂吧...orz(其实加上外挂,速度提升很多) 用链式前向星保存边 ...

  5. POJ 1330 Nearest Common Ancestors 【最近公共祖先LCA算法+Tarjan离线算法】

    Nearest Common Ancestors Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 20715   Accept ...

  6. LCA之tarjan离线

    显然81篇题解是有点多了,不让我提交. 更为不好的是没有一篇详细的\(tarjan\)(不过我也不会写详细的). 不过\(tarjan\)并没有我们想象的那样难理解,时间也并不爆炸(巧妙的跳过难写二字 ...

  7. POJ 1986 Distance Queries 【输入YY && LCA(Tarjan离线)】

    任意门:http://poj.org/problem?id=1986 Distance Queries Time Limit: 2000MS   Memory Limit: 30000K Total ...

  8. HDU2874【LCA(模板)】

    第一题LCA,代码参考自:Ice_Crazy 思路: 这个最短路算法是想都别想了,可以看出这幅图就是树嘛,那么对于查询就是求树上两个结点最短距离. 这里就是利用LCA的tarjan离线算法. 算法的大 ...

  9. POJ 1470 Closest Common Ancestors (模板题)(Tarjan离线)【LCA】

    <题目链接> 题目大意:给你一棵树,然后进行q次询问,然后要你统计这q次询问中指定的两个节点最近公共祖先出现的次数. 解题分析:LCA模板题,下面用的是离线Tarjan来解决.并且为了代码 ...

随机推荐

  1. xxx was built without full bitcode" 编译错误解决

    xxx was built without full bitcode" 编译错误解决 iOS 打包上线 All object files and libraries for bitcode ...

  2. 【二叉树的递归】06填充每个节点中的下一个正确的指针【Populating Next Right Pointers in Each Node】

    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 给定一个二叉树 struct Tr ...

  3. BLOB (binary large object)

    BLOB (binary large object),二进制大对象,是一个可以存储二进制文件的容器. 在计算机中,BLOB常常是数据库中用来存储二进制文件的字段类型. BLOB是一个大文件,典型的BL ...

  4. CRtmpServer

    1.  前言 crtmpserver是一个由C++语言编写的开源的RTMP流媒体服务器,官方网站是www.rtmpd.com 2.   CRtmpServer编译   2.1.  Win7+Vs201 ...

  5. luogu1353 Running

    dp[i][j]表示走i分钟疲劳值为j时的最远距离 然后搞一下就好啦 #include <iostream> #include <cstdio> #include <al ...

  6. shader学习推荐

    <DirectX 9.0 3D游戏开发编程基础> 当您理解了如何实现顶点着色器和像素着色器之后,接下来您可能想进一步了解使用这两种着色器能够实现哪些效果. 最好的方式就是研究一下现有的各种 ...

  7. Guice总结

    Guice总结 Jar包:guice-4.1.0.jar 辅包: guava-15.0.jar aopalliance-.jar javaee-api-6.0-RC2.jar Guice的IoC 两种 ...

  8. 孤独地、凄惨地AK

    一个\(OIer\)要写多少\(for\) 才能被称为一个\(OIer\) 一位巨佬要爆过多少次零 才能在省选逆袭 手指要多少次掠过键盘 才能安心地休息 \(OI\)啊 我的朋友 在风中\(AK\) ...

  9. const关键字祥解

    为什么使用const?采用符号常量写出的代码更容易维护:指针常常是边读边移动,而不是边写边移动:许多函数参数是只读不写的.const最常见用途是作为数组的界和switch分情况标号(也可以用枚举符代替 ...

  10. 记录对定时任务调度器的小小改进 - API调度计划

    之前记录过一篇 [开源一个定时任务调度器 webscheduler],这是一个看似简单的小工具,昨天部署到服务器上开始试用下,听听反馈. 项目经理看过后,立马反馈说这个使用 Cron表达式 的计划太难 ...