题目链接: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. 原生js监听input值改变事件

    哈哈哈,又来了,今天闲来无事,实验了下原生js监听input value值改变事件,下面就来说道说道: 本来写监听input值便获是用jquery的,之前的随笔写了,就是这个方法,地址:http:// ...

  2. C++ 0X 新特性实例(比较常用的) (转)

    转自:http://www.cnblogs.com/mrblue/p/3141456.html //array #include <array> void Foo1() { array&l ...

  3. 【IPC通信】key_t键和ftok函数

    System V IPC分为三种: System V消息队列 System V信号量 System V共享内存区 这三种类型的IPC使用key_t值做为它们的名字. key_t这个数据类型在<s ...

  4. Gym - 100851G:Generators(人尽皆知但是WA题)

    题意:现在有函数,每一项Xi=(A*X(i-1)+B)%C.现在给定N个函数以及K:X0,A,B,C.然你再每个函数选择一个数,使得其和最大,而且不被K整除. X0,A,B,C<=1e3 :K& ...

  5. android SDK manager 无法获取更新版本列表【转载】

    http://mirrors.neusoft.edu.cn/eclipse/releases/luna/打开这个网址就可以看到adt的详细信息:  http://developer.android.c ...

  6. ACM学习历程—HDU5422 Rikka with Graph(贪心)

    Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situation, so he ...

  7. bzoj 4199: [Noi2015]品酒大会 后缀树

    题目大意: 给定一个长为n的字符串,每个下标有一个权\(w_i\),定义下标\(i,j\)是r相似的仅当\(r \leq LCP(suf(i),suf(j))\)且这个相似的权为\(w_i,w_j\) ...

  8. Codeforces Round #402 (Div. 2) 题解

    Problem A: 题目大意: 给定两个数列\(a,b\),一次操作可以交换分别\(a,b\)数列中的任意一对数.求最少的交换次数使得任意一个数都在两个序列中出现相同的次数. (\(1 \leq a ...

  9. HDOJ(1069)最长下降子序列

    每个箱子可有3种叠加方式,所以有3*n个箱子.将箱子按长度由大到小排序,有求箱子按宽度的最长下降子序列的高度之和即可. #include<cstdio> #include<algor ...

  10. mongodb 的命令操作(转)

    成功启动MongoDB后,再打开一个命令行窗口输入mongo,就可以进行数据库的一些操作. 输入help可以看到基本操作命令: show dbs:显示数据库列表  show collections:显 ...