题意简述:给定一个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. Android重写view时onAttachedToWindow () 和 onDetachedFromWindow ()

    在重写View的时候,会遇到这两个方法 protected void onAttachedToWindow() Description copied from class: View This is ...

  2. K-L变换

    K-L变换( Karhunen-Loeve Transform)是建立在统计特性基础上的一种变换,有的文献也称为霍特林(Hotelling)变换,因他在1933年最先给出将离散信号变换成一串不相关系数 ...

  3. Solidworks输出Autocad的DWG格式乱码怎么办

    Solidworks输出DWG会有很多问题,如果没必要就别这么做,比如你只是想要打印图纸,Solidworks也可以直接打印,而且很方便,不需要转成DWG再打印,如果对方确实需要DWG格式的图纸,你只 ...

  4. nyoj43 24 Point game(DFS)

    题目43 题目信息 pid=43" style="text-decoration:none; color:rgb(55,119,188)">执行结果 本题排行 讨论 ...

  5. 新建 .NET Core 控制台项目 C# 数组深拷贝

    新建 .NET Core 控制台项目 1. 安装 .NET Core SDK 1.0 参考微软官方网站 https://www.microsoft.com/net/download/windows 2 ...

  6. Long-term stable release maintenance

    http://en.wikipedia.org/wiki/Linux_kernel 2014.5.28 2.6.32 2 December 2009[122] 2.6.32.62[123] Willy ...

  7. Django-mysq数据库链接问题

    Django链接MySQL数据库有可能会报no model named MySQLdb, 解决办法: 首先安装pymysql 然后进入到项目目录,找到__init__.py文件,在里面添加 impor ...

  8. java ArrayList倒序

    用Collections.reverse(list)即可.如:List<String> list = Arrays.asList(new String[] {"aa", ...

  9. TXT文本写入数据库

    load data local infile "D:/abc.txt" into table lee; leedabao.txt内容如下,中间用Tab隔开: 2 yuanpeng ...

  10. 线程中调用Updatedata的问题

    随便发个自定义消息,然后在 CMyDialog的自定义消息处理函数中 UpdateDate().因为 UpdateDate用到了线程本地存储.不能跨线程的 UpdateData只能在主线程中使用,将U ...