poj3728(lca / tarjan离线)

题目链接: http://poj.org/problem?id=3728

题意: 给出一棵带点权值的树, 对于 q 组形如 x, y 的询问, 一个人要从 x 到 y(单向), 他可以在路上任意一点以此点的的权值买一件物品, 并在接下来的路程中任意一点将其以该点的权值卖出, 输出其最大收益, 若不能获益则输出 0 .

思路: 若不考虑时间复杂度的话对于询问 x, y. 可以先求出 lca(x, y), 然后再 x -> lca -> y 路径上求一下最大收益即可. 然而这样会 tle.

可以考虑一下化简后面部分.

对于一组询问 x, y. 最大收益有 3 种情况:

1. x -> lca 过程中取得最大值

2. lca -> y 过程中取得最大值

3. lca -> y 中的最大权值减去 x -> lca 中的最小权值

为了方便处理这 3 种情况, 分别取

up 数组, up[x] 为 x -> lca 过程中能取得的最大值

dow 数组, dow[x] 为 lca -> x 过程中能取得的最大值

mi 数组, mi[x] 为 x -> lca 过程中最小权值

mx 数组, mx[x] 为 x -> lca 过程中最大权值

则第三种情况为: mx[y] - mi[x]

那么 x, y 的最大收益可以表示为: max(max(up[x], dow[y]), mx[y] - mi[x])

然而如果对于每一组 x, y 都更新一遍 up, dow, mi, mx 数组的话, 显然也是会 tle 的, 但是我们并没有必要那样做.

若对于询问 x, y, 我们已经的到了对应的 up, dow, mi, mx 数组, 那么对于 lca 在 lca(x, y) 上面的询问, 即对于 lca 为 lca(x, y) 祖先节点的询问 x1, x2,

假设 lca(x, y) 在 lca(x1, y1) 左子树中, 我们可以只更新 lca(x, y) 和 y1 到 lca(x1, y1) 对应的数组即可, 而无需更新 x1, y1 到 lca(x1, y1) 对应的数组.

如此, 我们可以按照询问的 lca 值从树的底层向上更新答案, 只需遍历一次树即可.

具体操作为; 在 tarjan 递归过程中先处理出所有询问的 lca, 然后在回溯时按 lca 自底向上更新答案即可.

代码:

  1 #include <iostream>
  2 #include <stdio.h>
  3 #include <string.h>
  4 using namespace std;
  5
  6 const int MAXN = 5e4 + 10;
  7 struct node{
  8     int v, ip, next;
  9     node(){};
 10     node(int V, int IP, int NEXT) : v(V), ip(IP), next(NEXT){};
 11 }edge1[MAXN << 1], edge2[MAXN << 1], edge3[MAXN << 1];
 12
 13 int pre[MAXN], a[MAXN], b[MAXN];
 14 int head1[MAXN], head2[MAXN], head3[MAXN], id1, id2, id3;
 15 int vis[MAXN], up[MAXN], dow[MAXN], mi[MAXN], mx[MAXN], sol[MAXN];
 16
 17 void init(void){
 18     memset(vis, 0, sizeof(vis));
 19     memset(head1, -1, sizeof(head1));
 20     memset(head2, -1, sizeof(head2));
 21     memset(head3, -1, sizeof(head3));
 22     id1 = id2 = id3 = 0;
 23 }
 24
 25 void addedge1(int u, int v, int ip){
 26     edge1[id1] = node(v, ip, head1[u]);
 27     head1[u] = id1++;
 28 }
 29
 30 void addedge2(int u, int v, int ip){
 31     edge2[id2] = node(v, ip, head2[u]);
 32     head2[u] = id2++;
 33 }
 34
 35 void addedge3(int u, int v, int ip){
 36     edge3[id3] = node(v, ip, head3[u]);
 37     head3[u] = id3++;
 38 }
 39
 40 int find(int v){//压缩路径并处理路径上的 up, dow, mi, mx 数组
 41     if(v == pre[v]) return v;//到叶子节点或者下面的lca时停止递归
 42     int fa = pre[v];
 43     pre[v] = find(pre[v]);
 44     up[v] = max(max(up[v], up[fa]), mx[fa] - mi[v]);//以 v -> lca 为入边
 45     dow[v] = max(max(dow[v], dow[fa]), mx[v] - mi[fa]);//以 lca -> v 为出边
 46     mi[v] = min(mi[v], mi[fa]);
 47     mx[v] = max(mx[v], mx[fa]);
 48     return pre[v];
 49 }
 50
 51 void tarjan(int u){
 52     vis[u] = 1;
 53     pre[u] = u;
 54     for(int i = head2[u]; i != -1; i = edge2[i].next){
 55         int v = edge2[i].v;
 56         int ip = edge2[i].ip;
 57         if(vis[v]){
 58             int f = find(v);
 59             addedge3(f, v, ip);//记录以 f 为 lca 的询问
 60         }
 61     }
 62     for(int i = head1[u]; i != -1; i = edge1[i].next){
 63         int v = edge1[i].v;
 64         if(!vis[v]){
 65             tarjan(v);
 66             pre[v] = u;
 67         }
 68     }
 69     for(int i = head3[u]; i != -1; i = edge3[i].next){//回溯时计算以 u 为 lca 的询问
 70         int ip = edge3[i].ip;
 71         find(a[ip]);
 72         find(b[ip]);
 73         sol[ip] = max(max(up[a[ip]], dow[b[ip]]), mx[b[ip]] - mi[a[ip]]);
 74     }
 75 }
 76
 77 int main(void){
 78     int n, x, y, q;
 79     while(~scanf("%d", &n)){
 80         init();
 81         for(int i = 1; i <= n; i++){
 82             scanf("%d", &x);
 83             up[i] = dow[i] = 0;
 84             mi[i] = mx[i] = x;
 85         }
 86         for(int i = 1; i < n; i++){
 87             scanf("%d%d", &x, &y);
 88             addedge1(x, y, i);
 89             addedge1(y, x, i);
 90         }
 91         scanf("%d", &q);
 92         for(int i = 0; i < q; i++){
 93             scanf("%d%d", &x, &y);
 94             a[i] = x;
 95             b[i] = y;
 96             addedge2(x, y, i);
 97             addedge2(y, x, i);
 98         }
 99         tarjan(1);
100         for(int i = 0; i < q; i++){
101             printf("%d\n", sol[i]);
102         }
103     }
104     return 0;
105 }

时间: 2024-10-27 13:27:56

poj3728(lca / tarjan离线)的相关文章

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

POJ 1330 最近公共祖先LCA(Tarjan离线做法)

题目链接:http://poj.org/problem?id=1330 题目大意十分明了简单,就是给定一棵树,求某两个子节点的最近公共祖先,如果尚不清楚LCA的同学,可以左转百度等进行学习. 稍微需要注意的是,建树顺序需要按照题目给定的顺序进行,也就是说根被设定成第一个给出的结点,如样例2. 此题网上题解颇多,但是多是使用的邻接表存图,于是我这里采用了边表,不过实质上Tarjan的部分思想都是一样的,均利用了并查集. AC代码: #include <cstdio> #include <c

POJ 1330 Nearest Common ancesters(LCA,Tarjan离线算法)

Description: In the figure, each node is labeled with an integer from {1, 2,...,16}. Node 8 is the root of the tree. Node x is an ancestor of node y if node x is in the path between the root and node y. For example, node 4 is an ancestor of node 16.

ZOJ_3195_Design the city(LCA+tarjan)

Design the city Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %lld & %llu Submit Status Description Cerror is the mayor of city HangZhou. As you may know, the traffic system of this city is so terrible, that there are traffic jams eve

POJ_1986_Distance Queries(LCA+tarjan)

Distance Queries Time Limit: 2000MS   Memory Limit: 30000KB   64bit IO Format: %I64d & %I64u Submit Status Description Farmer John's cows refused to run in his marathon since he chose a path much too long for their leisurely lifestyle. He therefore w

HDU_2586 &amp;&amp; HDU_2874 (LCA+tarjan)

How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 8541    Accepted Submission(s): 2997 Problem Description There are n houses in the village and some bidirectional roads connecting

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

HDU 2586 How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 11320    Accepted Submission(s): 4119 Problem Description There are n houses in the village and some bidirectional roads c

SPOJ 10628 Count on a tree(Tarjan离线LCA+主席树求树上第K小)

COT - Count on a tree #tree You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight. We will ask you to perform the following operation: u v k : ask for the kth minimum weight on the path from node u 

hdoj 2586 How far away ? 【Tarjan离线LCA】

题目:hdoj 2586 How far away ? 题意:给出一个有权树,求任意两点的之间的距离. 分析:思想就是以一个点 root 作为跟变成有根数,然后深搜处理处所有点到跟的距离.求要求的两个点的LCA(最近公共祖先), 然后ans = dis[x] + dis[y] - 2 * dis[LCA(x,y)],可以画图分析一下就知道. 求LCA我用的是Tarjan离线lca,由于询问次数很多,所以这个比较快. AC代码: #include <iostream> #include <