zoj3195(lca / RMQ离线)

题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3195

题意: 给出一棵 n 个节点的带边权的树, 有 q 组形如 x, y, z 的询问, 输出 x, y, z之间的最短路径.

思路: 在纸上画下不难发现 x, y, z之间的最短路径就是 x, y, z 两两之间的最短路径和的一半.

我们可以通过 lca 模板求出 x, y, z 两两之间的最短路径, 然后再算下 x, y, z三点之间的最短路径即可.

这题应该是用 RMQ 在线比较好写一点, 用 tarjan 的话记录路径有点麻烦.

代码:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <math.h>
 5 using namespace std;
 6
 7 const int MAXN = 5e4 + 10;
 8 struct node{
 9     int v, w, next;
10 }edge[MAXN << 1];
11
12 int dp[MAXN << 1][30];
13 int ver[MAXN << 1], deep[MAXN << 1], first[MAXN];
14 int dis[MAXN], head[MAXN], vis[MAXN], indx, ip;
15
16 inline void init(void){
17     memset(vis, 0, sizeof(vis));
18     memset(head, -1, sizeof(head));
19     indx = 0;
20     ip = 0;
21 }
22
23 void addedge(int u, int v, int w){
24     edge[ip].v = v;
25     edge[ip].w = w;
26     edge[ip].next = head[u];
27     head[u] = ip++;
28 }
29
30 void dfs(int u, int h){
31     vis[u] = 1;
32     ver[++indx] = u;
33     deep[indx] = h;
34     first[u] = indx;
35     for(int i = head[u]; i != -1; i = edge[i].next){
36         int v = edge[i].v;
37         if(!vis[v]){
38             dis[v] = dis[u] + edge[i].w;
39             dfs(v, h + 1);
40             ver[++indx] = u;
41             deep[indx] = h;
42         }
43     }
44 }
45
46 void ST(int n){
47     for(int i = 1; i <= n; i++){
48         dp[i][0] = i;
49     }
50     for(int j = 1; (1 << j) <= n; j++){
51         for(int i = 1; i + (1 << j) - 1 <= n; i++){
52             int x = dp[i][j - 1], y = dp[i + (1 << (j - 1))][j - 1];
53             dp[i][j] = deep[x] < deep[y] ? x : y;
54         }
55     }
56 }
57
58 int RMQ(int l, int r){
59     int len = log2(r - l + 1);
60     int x = dp[l][len], y = dp[r - (1 << len) + 1][len];
61     return deep[x] < deep[y] ? x : y;
62 }
63
64 int LCA(int x, int y){
65     int l = first[x], r = first[y];
66     if(l > r) swap(l, r);
67     int pos = RMQ(l, r);
68     return ver[pos];
69 }
70
71 int main(void){
72     bool flag = false;
73     int n, q, x, y, z;
74     while(~scanf("%d", &n)){
75         if(flag) puts("");
76         flag = true;
77         init();
78         for(int i = 1; i < n; i++){
79             scanf("%d%d%d", &x, &y, &z);
80             addedge(x, y, z);
81             addedge(y, x, z);
82         }
83         dis[1] = 0;
84         dfs(1, 1);
85         ST(2 * n - 1);
86         scanf("%d", &q);
87         while(q--){
88             scanf("%d%d%d", &x, &y, &z);
89             int lca1 = LCA(x, y);
90             int lca2 = LCA(x, z);
91             int lca3 = LCA(y, z);
92             int sol1 = dis[x] + dis[y] - 2 * dis[lca1];
93             int sol2 = dis[x] + dis[z] - 2 * dis[lca2];
94             int sol3 = dis[y] + dis[z] - 2 * dis[lca3];
95             printf("%d\n", (sol1 + sol2 + sol3) >> 1);
96         }
97     }
98     return 0;
99 }

时间: 2024-08-09 22:39:47

zoj3195(lca / RMQ离线)的相关文章

暑假集训 || LCA &amp;&amp; RMQ

LCA定义为对于一颗树 树上两个点的最近公共祖先 一.Tarjan求LCA(离线方法 https://blog.csdn.net/lw277232240/article/details/77017517 二.倍增法求LCA void dfs(int u, int f) { for(int i = 1; i <= 18; i++) if(deep[u] >= (1<<i)) fa[u][i] = fa[fa[u][i-1]][i-1]; for(int i = head[u];i;i

UESTC 912 树上的距离 --LCA+RMQ+树状数组

1.易知,树上两点的距离dis[u][v] = D[u]+D[v]-2*D[lca(u,v)] (D为节点到根节点的距离) 2.某条边<u,v>权值一旦改变,将会影响所有以v为根的子树上的节点到根节点的距离,很明显,DFS一遍后以v为根的子树在DFS序列中是连续的一段,及转化为区间更新问题,可以用树状数组. 做法:先把求LCA解决,LCA可以转化为RMQ问题,可参见:LCA转RMQ, 即转化为LCA(T,u,v) = RMQ(B,pos[u],pos[v]),其中B为深度序列.预先DFS可以处

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

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2874 题意: 给出 n 个顶点 m 条边的一个森林, 有 k 个形如 x y 的询问, 输出 x, y 之间的最短路径. 思路: 如果将森林换成一棵树的话就是一道 lca 模板题了, 不过本题需要稍作改动. 解法1: tarjan 只需要先判断一下 x, y 是否在一颗树里面就 ok 了, 不过这道题的询问有点多, 很容易 mle. 代码: 1 #include <iostream> 2 #in

学习 LCA&amp;&amp;RMQ

参考:点击打开链接 点击打开链接      点击打开链接(一些总结) 点击打开链接(不错的模板) 题目:点击打开链接 花了4天时间做完了这个专题,LCA的问题用处还是很大,同时能体会RMQ的ST算法中dp的味道.基本方法就是ST,LCA转RMQ,LCA的Tarjan,LCA倍增(这个可存储边权) 这个专题后面四道题都非常好,推荐大家做做. 细节: 1. ST方法2^i 包含自己,因此其真实只包含到i+2^k-1的范围. 2. Tarjan一般都很快,但不适合修改类型的问题,关于权值长度之类的,S

LCA&amp;&amp;RMQ问题

参考:点击打开链接 点击打开链接      点击打开链接(一些总结) 点击打开链接(不错的模板) 题目:点击打开链接 花了4天时间做完了这个专题,LCA的问题用处还是很大,同时能体会RMQ的ST算法中dp的味道.基本方法就是ST,LCA转RMQ,LCA的Tarjan,LCA倍增(这个可存储边权) 这个专题后面四道题都非常好,推荐大家做做. 细节: 1. ST方法2^i 包含自己,因此其真实只包含到i+2^k-1的范围. 2. Tarjan一般都很快,但不适合修改类型的问题,关于权值长度之类的,S

bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1272  Solved: 451[Submit][Status][Discuss] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. 有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[

POJ 1470 Closest Common Ancestors(LCA&amp;RMQ)

题意比较费劲:输入看起来很麻烦.处理括号冒号的时候是用%1s就可以.还有就是注意它有根节点...Q次查询,我是用在线st做的. /************************************************************************* > File Name: 3.cpp > Author: Howe_Young > Mail: [email protected] > Created Time: 2015年10月08日 星期四 19时03分

POJ-1986 Distance Queries(LCA、离线)

Distance Queries Time Limit: 2000MS   Memory Limit: 30000K Total Submissions: 14378   Accepted: 5062 Case Time Limit: 1000MS Description Farmer John's cows refused to run in his marathon since he chose a path much too long for their leisurely lifesty

poj2763(lca / RMQ + 线段树)

题目链接: http://poj.org/problem?id=2763 题意: 第一行输入 n, q, s 分别为树的顶点个数, 询问/修改个数, 初始位置. 接下来 n - 1 行形如 x, y, w 的输入为点 x, y 之间连边且边权为 w. 接下来 q 行输入, 若输入形式为 1 x y 则为将点 x 的权值修改为 y , 若输入形式为 0 x 则询问 s 到 x 的最短距离为多少. 上一组的 x 为下一组的 s. 思路: 若去掉修改边权部分, 则为一个 lca 模板题. 对于修改边权