题意简述:给定一个N个节点的树,1<=N<=50000 每个节点都有一个权值,代表商品在这个节点的价格。商人从某个节点a移动到节点b,且只能购买并出售一次商品,问最多可以产生多大的利润。

算法分析:显然任意两个城市之间的路径是唯一的,商人有方向地从起点移动到终点。询问这条路径上任意两点权值之差最大为多少,且要保证权值较大的节点在路径上位于权值较小的节点之后。

暴力的方法是显而易见的,只要找到两个点的深度最深的公共祖先,就等于找到了这条路径,之后沿着路径走一遍即可找到最大的利润,然而无法满足50000的数据规模。

首先考虑高效寻找LCA(公共祖先)的方法。记录ance[i][j]为节点i向上走2^j步到达的某个祖先。可以简单地列出方程 ance[i][j]=ance[ance[i][j-1]][j-1];于是找到了高效构建的方法。

每次寻找LCA 首先将两个节点通过swim(a,b)函数转移到同一深度,然后每次找一个最小的j使得ance[a][j]==ance[b][j] 之后将节点a赋值为ance[a][j-1] 直到j=0就找到了两者的LCA

现在我们已经找到了高效寻找LCA的方法,假设我们知道节点a到LCA的最小值minp[],LCA到节点b的最大值maxp[],

以及买卖地点全在LCA之前可以获得的最大利润maxi[] 以及买卖地点全在LCA之后可以获得的最大利润maxI[] 显然就得到了最后的答案。 维护这些数据的方式类似于维护ance数组的方式,DP方程也很好列出, 这里就不给出了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
using namespace std;
const int maxn=50000+10;
vector<int>po[maxn];
int dep[maxn],ance[maxn][17],maxp[maxn][17],minp[maxn][17],maxi[maxn][17],maxI[maxn][17],price[maxn],n,fa[maxn];
bool vis[maxn];
int max(int a,int b)
{if(a>b)return a;else return b;
}
int min(int a,int b)
{if(a>b)return b;else return a;
}
queue<int> q;
void BFS_build()
{memset(vis,0,sizeof(vis));q.push(1);fa[1]=1;dep[1]=1;vis[1]=true;while(!q.empty()){int np=q.front();q.pop();ance[np][0]=fa[np];maxp[np][0]=max(price[np],price[fa[np]]);minp[np][0]=min(price[np],price[fa[np]]);if(price[np]<price[fa[np]])maxi[np][0]=price[fa[np]]-price[np];else maxi[np][0]=0;if(price[np]>price[fa[np]])maxI[np][0]=price[np]-price[fa[np]];else maxI[np][0]=0;for(int i=1;i<=16;i++)//倍增DP方程 {   ance[np][i]=ance[ance[np][i-1]][i-1];maxp[np][i]=max(maxp[np][i-1],maxp[ance[np][i-1]][i-1]);minp[np][i]=min(minp[np][i-1],minp[ance[np][i-1]][i-1]);int a=maxi[np][i-1],b=maxi[ance[np][i-1]][i-1],c=0;c=maxp[ance[np][i-1]][i-1]-minp[np][i-1];maxi[np][i]=max(max(a,b),c);a=maxI[np][i-1];b=maxI[ance[np][i-1]][i-1];c;c=maxp[np][i-1]-minp[ance[np][i-1]][i-1];maxI[np][i]=max(max(a,b),c);if(ance[np][i]==1)break;}for(int i=0;i<po[np].size();i++){int nv=po[np][i];if(vis[nv])continue;fa[nv]=np;dep[nv]=dep[np]+1;q.push(nv);vis[nv]=true;}}
}
int ia,ib,mi,ma;
int ancest;
void swim(int &a,int &b)
{if(dep[a]==dep[b])return ;while(dep[a]>dep[b]){int i;for(i=0;i<=16;i++){if(pow(2,i)+dep[b]>dep[a])break;}ia=max(max(ia,maxi[a][i-1]),maxp[a][i-1]-mi);mi=min(mi,minp[a][i-1]);a=ance[a][i-1];}while(dep[a]<dep[b]){int i;for(i=0;i<=16;i++){if(pow(2,i)+dep[a]>dep[b])break;}ib=max(max(ib,maxI[b][i-1]),ma-minp[b][i-1]);ma=max(ma,maxp[b][i-1]);b=ance[b][i-1];}
}
int solve(int a,int b)
{ia=0;ib=0;mi=price[a];ma=price[b];swim(a,b);if(a==b)return max(max(ia,ib),ma-mi);while(true){int i;for(i=0;i<=16;i++){if(ance[a][i]==ance[b][i])break;}if(i==0){ancest=ance[a][0];ia=max(ia,price[ancest]-mi);ib=max(ib,ma-price[ancest]);mi=min(mi,price[ancest]);ma=max(ma,price[ancest]);return max(max(ia,ib),ma-mi);}else{ia=max(max(ia,maxi[a][i-1]),maxp[a][i-1]-mi);ib=max(max(ib,maxI[b][i-1]),ma-minp[b][i-1]);mi=min(mi,minp[a][i-1]);ma=max(ma,maxp[b][i-1]);a=ance[a][i-1];b=ance[b][i-1];}}}
int main()
{freopen("t.txt","r",stdin);scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&price[i]);for(int i=1;i<n;i++){int a,b;scanf("%d%d",&a,&b);po[a].push_back(b);po[b].push_back(a);}BFS_build();int p;scanf("%d",&p);for(int i=1;i<=p;i++){int a,b;scanf("%d%d",&a,&b);printf("%d\n",solve(a,b)); }return 0;
}

  这个题目似乎是北大月赛的题目,不得不佩服他们题目的质量,渣校只能仰望了。

转载于:https://www.cnblogs.com/heisenberg-/p/5471202.html

POJ3728 THE MERCHANT LCA RMQ DP相关推荐

  1. poj 3728 The merchant// lca(倍增实现) + dp || tarjan+并查集路径上dp

    poj 3728 The merchant// lca(倍增实现) + dp Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: ...

  2. hdu 5266 pog loves szh III LCA+RMQ

    题意: 给你一棵树,然后询问l~r节点的最近公共祖先(LCA). 思路: 用RMQ维护一段区间的LCA,然后询问时,将两个区间的LCA再求一次LCA即可. code: #pragma comment( ...

  3. 倍增算法入门 超详细解答+LCA+RMQ(ST表)+例题剖析

    目录 一.倍增算法 二.倍增算法的应用:求LCA(最近公共祖先)附模板题 三.倍增算法的应用:RMQ 问题(ST表)附模板题 一.倍增算法 要了解倍增之前,强烈建议大家先看一下这位大佬对倍增的解释:[ ...

  4. LCA RMQ+ST表学习笔记

    RMQ RMQ问题:在给定的一个长度位N的区间中,有M个询问,每次询问给出区间[L,R],求出区间段元素的 最大值/最小值.对于RMQ问题很容易想到遍历的做法,将区间[L,R]中的元素遍历一遍,即可寻 ...

  5. 倍增算法入门 超详细解答+LCA+RMQ(ST表)+例题剖析

    目录 一.倍增算法 二.倍增算法的应用:求LCA(最近公共祖先)附模板题 三.倍增算法的应用:RMQ 问题(ST表)附模板题 一.倍增算法 要了解倍增之前,强烈建议大家先看一下这位大佬对倍增的解释:[ ...

  6. LCA RMQ模版

    RMQ(space table算法) /* RMQ(Range Minimum/Maximum Query)问题:RMQ问题是求给定区间中的最值问题.当然,最简单的算法是O(n)的,但是对于查询次数很 ...

  7. 【BZOJ3700】发展城市 [LCA][RMQ]

    发展城市 Time Limit: 20 Sec  Memory Limit: 512 MB [Submit][Status][Discuss] Description 众所周知,Hzwer学长是一名高 ...

  8. BZOJ - 5488 - Teamwork(RMQ + dp)

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=5488 思路:可以直接也可以. 思路:定义表示前  个数分组的最优解,明显可以从转移到,其 ...

  9. HDU 5266 pog loves szh III【LCA RMQ】

    B - pog loves szh III 题目:添加链接描述 题意:找出区域l到r的LCA->找l和r的LCA 分析: 链式前向星存树,先用dfs处理结点倍增关系. 然后从循环处理较深结点,直 ...

最新文章

  1. Asp.net支持的最大上传文件大小
  2. 一个不错的资源共享微盘
  3. 编译libav(ffmpeg)库
  4. Eclipse创建JavaWeb工程
  5. python基础(一)简单入门
  6. mysql 第二大的数据_MYSQL – 从大表中的第二行中选择数据
  7. Android 位置服务——BaiduLocation的使用
  8. 数据库MySQL/mariadb知识点——触发器
  9. 工作组服务器操作系统,工作组服务器操作系统
  10. c语言snprintf函数简介
  11. 测试金士顿固态硬盘软件,金士顿固态硬盘优化工具(Kingston Toolbox)
  12. 华为防火墙ssl xxx配置
  13. 程序员的自我修养之数学基础05:线性方程组解的情况(矩阵的初等变换和高斯消元法)
  14. C++使用模板重载vector的加减法实现矩阵向量加减法
  15. 安全芯片介绍-身份认证加密芯片方案
  16. 高斯-赛德尔(Gauss-Seidel)解线性方程组的Matlab实现
  17. cad怎么画立体图形教学_cad怎么绘立体图?
  18. QDataStream类的官方简介
  19. 新媒体运营教程:完整的用户增长5步方案!
  20. PHP制作个人名片二维码

热门文章

  1. 使用Linux自定义自动补全命令完善自己的shell脚本
  2. 架构师要了解那些??
  3. php自动载入类文件函数,我可以在没有PHP的类中自动加载函数文件吗?
  4. java并发编程之CompletableFuture
  5. 计算机需要 更新 无法卸载,电脑更新变卡顿,只要卸载它就能解决?
  6. highCharts如何实现json数组数据的图形展示
  7. matlab sfunction,搭建最基本的S-function模块
  8. java extern的作用_学习笔记之20-static和extern关键字2-对变量的作用
  9. java 高德地图 车型比价计算_高德地图的高速公路过路费计算功能是如何实现的?有相应开放的API吗?...
  10. tt公路车Java配置怎么样_普通公路车换TT车把可以吗,别的东西还有需要换的吗?...