题目链接: 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 自底向上更新答案即可.

代码:

 #include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std; const int MAXN = 5e4 + ;
struct node{
int v, ip, next;
node(){};
node(int V, int IP, int NEXT) : v(V), ip(IP), next(NEXT){};
}edge1[MAXN << ], edge2[MAXN << ], edge3[MAXN << ]; int pre[MAXN], a[MAXN], b[MAXN];
int head1[MAXN], head2[MAXN], head3[MAXN], id1, id2, id3;
int vis[MAXN], up[MAXN], dow[MAXN], mi[MAXN], mx[MAXN], sol[MAXN]; void init(void){
memset(vis, , sizeof(vis));
memset(head1, -, sizeof(head1));
memset(head2, -, sizeof(head2));
memset(head3, -, sizeof(head3));
id1 = id2 = id3 = ;
} void addedge1(int u, int v, int ip){
edge1[id1] = node(v, ip, head1[u]);
head1[u] = id1++;
} void addedge2(int u, int v, int ip){
edge2[id2] = node(v, ip, head2[u]);
head2[u] = id2++;
} void addedge3(int u, int v, int ip){
edge3[id3] = node(v, ip, head3[u]);
head3[u] = id3++;
} int find(int v){//压缩路径并处理路径上的 up, dow, mi, mx 数组
if(v == pre[v]) return v;//到叶子节点或者下面的lca时停止递归
int fa = pre[v];
pre[v] = find(pre[v]);
up[v] = max(max(up[v], up[fa]), mx[fa] - mi[v]);//以 v -> lca 为入边
dow[v] = max(max(dow[v], dow[fa]), mx[v] - mi[fa]);//以 lca -> v 为出边
mi[v] = min(mi[v], mi[fa]);
mx[v] = max(mx[v], mx[fa]);
return pre[v];
} void tarjan(int u){
vis[u] = ;
pre[u] = u;
for(int i = head2[u]; i != -; i = edge2[i].next){
int v = edge2[i].v;
int ip = edge2[i].ip;
if(vis[v]){
int f = find(v);
addedge3(f, v, ip);//记录以 f 为 lca 的询问
}
}
for(int i = head1[u]; i != -; i = edge1[i].next){
int v = edge1[i].v;
if(!vis[v]){
tarjan(v);
pre[v] = u;
}
}
for(int i = head3[u]; i != -; i = edge3[i].next){//回溯时计算以 u 为 lca 的询问
int ip = edge3[i].ip;
find(a[ip]);
find(b[ip]);
sol[ip] = max(max(up[a[ip]], dow[b[ip]]), mx[b[ip]] - mi[a[ip]]);
}
} int main(void){
int n, x, y, q;
while(~scanf("%d", &n)){
init();
for(int i = ; i <= n; i++){
scanf("%d", &x);
up[i] = dow[i] = ;
mi[i] = mx[i] = x;
}
for(int i = ; i < n; i++){
scanf("%d%d", &x, &y);
addedge1(x, y, i);
addedge1(y, x, i);
}
scanf("%d", &q);
for(int i = ; i < q; i++){
scanf("%d%d", &x, &y);
a[i] = x;
b[i] = y;
addedge2(x, y, i);
addedge2(y, x, i);
}
tarjan();
for(int i = ; i < q; i++){
printf("%d\n", sol[i]);
}
}
return ;
}

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

  1. 最近公共祖先LCA Tarjan 离线算法

    [简介] 解决LCA问题的Tarjan算法利用并查集在一次DFS(深度优先遍历)中完成所有询问.换句话说,要所有询问都读入后才开始计算,所以是一种离线的算法. [原理] 先来看这样一个性质:当两个节点 ...

  2. HDU-2586-How far away(LCA Tarjan离线算法)

    链接:https://vjudge.net/problem/HDU-2586 题意: 勇气小镇是一个有着n个房屋的小镇,为什么把它叫做勇气小镇呢,这个故事就要从勇气小镇成立的那天说起了,修建小镇的时候 ...

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

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

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

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

  5. 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  ...

  6. LCA问题的ST,tarjan离线算法解法

    一  ST算法与LCA 介绍 第一次算法笔记这样的东西,以前学算法只是笔上画画写写,理解了下,刷几道题,其实都没深入理解,以后遇到新的算法要把自己的理解想法写下来,方便日后回顾嘛>=< R ...

  7. LCA 最近公共祖先 tarjan离线 总结 结合3个例题

    在网上找了一些对tarjan算法解释较好的文章 并加入了自己的理解 LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点.也就是说,在两个点通 ...

  8. 【HDOJ2586】【Tarjan离线求LCA】

    http://acm.hdu.edu.cn/showproblem.php?pid=2586 How far away ? Time Limit: 2000/1000 MS (Java/Others) ...

  9. LCA最近公共祖先(Tarjan离线算法)

    这篇博客对Tarjan算法的原理和过程模拟的很详细. 转载大佬的博客https://www.cnblogs.com/JVxie/p/4854719.html 第二次更新,之前转载的博客虽然胜在详细,但 ...

随机推荐

  1. DELPHI线程例子-FC

    {优秀的数据库应用应当充分考虑数据库访问的速度问题.通常可以通过优化数据库.优化 查询语句.分页查询等途径收到明显的效果.即使是这样,也不可避免地会在查询时闪现一个带有 SQL符号的沙漏,即鼠标变成了 ...

  2. Spring4自动装配(default-autowire) (转)

    原文地址:http://blog.csdn.net/conglinyu/article/details/63684957 Spring 自动装配 通过配置default-autowire 属性,Spr ...

  3. utc时间、本地时间及时间戳转化

    1.时间戳的概念 时间戳的定义请看百科unix时间戳,需要注意的时间戳为当前时刻减去UTC时间(1970.1.1)零点时刻的秒数差,与当前系统所处的时区无关,同一时刻不管在任何时区下得到的时间戳都是一 ...

  4. 如果你的NavigationDrawer里面的Item没有响应,Drawer不能左滑关闭

    如果你的NavigationDrawer里面的Item没有响应,Drawer不能左滑关闭,应该是因为你没有把主要内容放在DrawerLayout标签下的第一位. The main content vi ...

  5. qduoj 218 签到题

    Description a坤和大明在一块由n个方块组成的棋盘(1 × n)上做游戏.一开始a坤在棋盘上放了k个矩形并且没有告诉大明具体位置.每个矩形都占a个连续方块(1 × a),任意两个矩形不可重叠 ...

  6. bzoj 2342: 双倍回文 回文自动机

    题目大意: 定义双倍回文串的左一半和右一半均是回文串的长度为4的倍数的回文串 求一个给定字符串中最长的双倍回文串的长度 题解: 我们知道可以简单地判定以某一点结尾的最长回文串 我们知道可以简单地判定以 ...

  7. openfire存储中文字符乱码解决办法

    转载于: Xmpp问题总结:处理Openfire 中文乱码问题(2) openfire是一个非常不错的IM服务器,而且是纯Java实现,具有多个平台的版本,他的数据存储可以采用多种数据库,如MySQL ...

  8. 【转】 Pro Android学习笔记(七七):服务(2):Local Service

    目录(?)[-] Local service代码 调用Local ServiceLocal Service client代码 AndroidManifestxml定义Serviceacitivty的l ...

  9. MyEclipse、Eclipse SVN插件的帐号、密码修改

    问题描述: Eclipse的SVN插件Subclipse做得很好,在svn操作方面提供了很强大丰富的功能.但到目前为止,该插件对svn用户的概念极为淡薄,不但不能方便地切换用户,而且一旦用户的帐号.密 ...

  10. js---倒计时的自动跳转.html

    ============================================================================== 倒计时的自动跳转.html <!DO ...