题意简述:给定一个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;
}

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

POJ3728 THE MERCHANT LCA RMQ DP的更多相关文章

  1. POJ3728 LCA RMQ DP

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

  2. [POJ3728]The merchant(tanrjan_lca + DP)

    传送门 比着题解写还错... 查了两个小时没查出来,心态爆炸啊 以后再查 ——代码(WA) #include <cstdio> #include <cstring> #incl ...

  3. 【Homework】LCA&RMQ

    我校是神校,作业竟然选自POJ,难道不知道“珍爱生命 勿刷POJ”么? 所有注明模板题的我都十分傲娇地没有打,于是只打了6道题(其实模板题以前应该打过一部分但懒得找)(不过感觉我模板还是不够溜要找个时 ...

  4. POJ 1470 Closest Common Ancestors(LCA&RMQ)

    题意比较费劲:输入看起来很麻烦.处理括号冒号的时候是用%1s就可以.还有就是注意它有根节点...Q次查询 在线st算法 /*************************************** ...

  5. CDOJ 92 Journey(LCA&RMQ)

    题目连接:http://acm.uestc.edu.cn/#/problem/show/92 题意:给定一棵树,最后给加一条边,给定Q次查询,每次查询加上最后一条边之后是否比不加这条边要近,如果近的话 ...

  6. POJ3417 LCA+树dp

    http://poj.org/problem?id=3417 题意:先给出一棵无根树,然后下面再给出m条边,把这m条边连上,然后每次你能毁掉两条边,规定一条是树边,一条是新边,问有多少种方案能使树断裂 ...

  7. 算法详解(LCA&RMQ&tarjan)补坑啦!完结撒花(。◕ˇ∀ˇ◕)

    首先,众所周知,求LCA共有3种算法(树剖就不说了,太高级,以后再学..). 1.树上倍增(ST表优化) 2.RMQ&时间戳(ST表优化) 3.tarjan(离线算法)不讲..(后面补坑啦!) ...

  8. POJ 2763 (LCA +RMQ+树状数组 || 树链部分) 查询两点距离+修改边权

    题意: 知道了一颗有  n 个节点的树和树上每条边的权值,对应两种操作: 0 x        输出 当前节点到 x节点的最短距离,并移动到 x 节点位置 1 x val   把第 x 条边的权值改为 ...

  9. poj3728之离线LCA+dp思想/RMQ+LCA(非常好的题目)

    题意很简单 给一个树(n < 5w) 每个点有个权值,代表商品价格 若干个询问(5w) 对每个询问,问的是从u点走到v点(简单路径),商人在这个路径中的某点买入商品,然后在某点再卖出商品,   ...

随机推荐

  1. 投资人王刚口述:滴滴如何用八十万成为百亿美金公司? zz

    作者|李好福布斯杂志中文版采编 阿里巴巴前高管.滴滴打车天使投资人王刚近日在杭州接受了<福布斯>独家专访,讲述了集齐“阿里的人.百度的技术.腾讯的钱”的滴滴如何从八十万启动资金,在三年内成 ...

  2. Go -- FIFO类(缓存淘汰算法)(转)

    1 FIFO 1.1. 原理 按照“先进先出(First In,First Out)”的原理淘汰数据. 1.2. 实现 FIFO队列,具体实现如下: 1. 新访问的数据插入FIFO队列尾部,数据在FI ...

  3. Java反射之Field用法

    在Java反射中Field用于获取某个类的属性或该属性的属性值 一:如何通过Field反射获取类的属性 Field提供如下几种方法: :1:Class.getDeclaredField(String ...

  4. .NET组件编程

    链接 http://www.cnblogs.com/mapserver/archive/2006/03/06/343632.html

  5. sql的一些知识

    查询 查询表中的所有列(所有信息) SELECT * FROM userinfo 查询某一列(指定) select name from userinfo 查询某一列(指定,去重) SELECT DIS ...

  6. 怎样高效利用GitHub(非常多资料可供下载)

    正是Github.让社会化编程成为现实.本文尝试谈谈GitHub的文化.技巧与影响. Q1:GitHub是什么 Q2:GitHub风格 Q3: 在GitHub.怎样跟牛人学习 Q4: 享受纯粹的写作与 ...

  7. python(39)- 网络编程socket练习

    基于tcp的套接字实现远程执行命令的操作 #服务端 import socket import subprocess phone=socket.socket(socket.AF_INET,socket. ...

  8. wdcp新开站点或绑定域名打不开或无法访问的问题

    一 用IP可以打开,但用域名打开网站显示到默认页面1  站点列表里是否有相应的网站信息 2  检查有没站点配置文件后台 >系统管理 >文件管理器 >虚拟主机站点文件(nginx,ap ...

  9. node.js如何读取MySQL数据

    先安装mysql模块. node.js默认安装时,模块文件放在 /usr/local/lib/node_modules 这个目录下,为了便宜管理,模块还是统一安装到这里好. $ cd /usr/loc ...

  10. 关于angular JS 中$timeOut 的一些不正常情况下的$destory

    最近项目中存在的问题头疼脑热了好一会. 我先简单说明下问题是由,使用$timeOut循环调用的时候由于页面存在异步加载会出现反复执行循环反复调用$timeOut,怎么清除跳出循环都不管用.于是查到了如 ...