POJ3728 LCA RMQ DP
题意简述:给定一个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 LCA RMQ DP的更多相关文章
- POJ3728 THE MERCHANT LCA RMQ DP
题意简述:给定一个N个节点的树,1<=N<=50000 每个节点都有一个权值,代表商品在这个节点的价格.商人从某个节点a移动到节点b,且只能购买并出售一次商品,问最多可以产生多大的利润. ...
- POJ 1470 Closest Common Ancestors(LCA&RMQ)
题意比较费劲:输入看起来很麻烦.处理括号冒号的时候是用%1s就可以.还有就是注意它有根节点...Q次查询 在线st算法 /*************************************** ...
- CDOJ 92 Journey(LCA&RMQ)
题目连接:http://acm.uestc.edu.cn/#/problem/show/92 题意:给定一棵树,最后给加一条边,给定Q次查询,每次查询加上最后一条边之后是否比不加这条边要近,如果近的话 ...
- 【Homework】LCA&RMQ
我校是神校,作业竟然选自POJ,难道不知道“珍爱生命 勿刷POJ”么? 所有注明模板题的我都十分傲娇地没有打,于是只打了6道题(其实模板题以前应该打过一部分但懒得找)(不过感觉我模板还是不够溜要找个时 ...
- POJ3417 LCA+树dp
http://poj.org/problem?id=3417 题意:先给出一棵无根树,然后下面再给出m条边,把这m条边连上,然后每次你能毁掉两条边,规定一条是树边,一条是新边,问有多少种方案能使树断裂 ...
- 算法详解(LCA&RMQ&tarjan)补坑啦!完结撒花(。◕ˇ∀ˇ◕)
首先,众所周知,求LCA共有3种算法(树剖就不说了,太高级,以后再学..). 1.树上倍增(ST表优化) 2.RMQ&时间戳(ST表优化) 3.tarjan(离线算法)不讲..(后面补坑啦!) ...
- POJ 2763 (LCA +RMQ+树状数组 || 树链部分) 查询两点距离+修改边权
题意: 知道了一颗有 n 个节点的树和树上每条边的权值,对应两种操作: 0 x 输出 当前节点到 x节点的最短距离,并移动到 x 节点位置 1 x val 把第 x 条边的权值改为 ...
- poj3417 LCA + 树形dp
Network Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 4478 Accepted: 1292 Descripti ...
- UESTC 912 树上的距离 --LCA+RMQ+树状数组
1.易知,树上两点的距离dis[u][v] = D[u]+D[v]-2*D[lca(u,v)] (D为节点到根节点的距离) 2.某条边<u,v>权值一旦改变,将会影响所有以v为根的子树上的 ...
随机推荐
- JS基础知识——定时器
1.定时器——setInterval("执行函数名",间隔时间); setInterval(fun,1000);每隔1秒钟执行一次fun函数 setInterval("f ...
- Tesseract-OCR使用记录
Tesseract是一个开源的OCR(Optical Character Recognition,光学字符识别)引擎,可以识别多种格式的图像文件并将其转换成文本,目前已支持60多种语言(包括中文). ...
- IE去掉input的type=”text”输入内容时出现的X和type=”password”出现的眼睛图标
从IE 10开始,type=”text” 的 input 在用户输入内容后,会自动产生一个小叉叉(X),方便用户点击清除已经输入的文本.对于type=”password”的 input 则会在右方显示 ...
- java 单元测试 注入
ApplicationContext appContext = new ClassPathXmlApplicationContext("appContext.xml"); MySQ ...
- Android控件系列之RadioButton&RadioGroup
学习目的: 1.掌握在Android中如何建立RadioGroup和RadioButton 2.掌握RadioGroup的常用属性 3.理解RadioButton和CheckBox的区别 4.掌握Ra ...
- Android L(5.0)源码之图形与图像处理之绘图——Canvas
最近在研究android 5.0的gallery模块,学习了相关的知识点,准备写点博客总结一下,有时间了会补充完整
- LPC2478的外部中断使用
LPC2478外部中断 2478的外部中断模型如下 也就是说,port0和2支持外部中断,EINT0-2是三个独立管脚的中断,而EINT3则是port0和2的所有中断共同拥有的向量 对于port0和2 ...
- Spring MVC之Action输入参数
第一部分:Action输入参数Spring MVC 通过@RequestMapping注解映射请求,最终的真正执行代码为处理器方法,即@RequestMapping注解的方法.Spring MVC方法 ...
- Oracle行转列的3种方法
测试表为A3 , 有5个字段:ID1, ID2, ID3, ID4, ID5 测试数据如下: ID1 ID2 ID3 ID4 ID5 1 2 3 4 5 11 22 33 44 55 111 222 ...
- python 错误AttributeError: 'module' object has no attribute 'AF_INET'
写了一个简单的python socket的程序.运行时,报错如下 原因:文件的命名与Python的function的命名冲突 修改名称后,发现还是无法运行,检查目录下面是否有 这样子的一个文件,删除即 ...