题目
数据约定:
对于30%的数据, 1≤N,M≤100
对于60%的数据,1≤N,M≤1000
对于100%的数据,1≤N,M≤50000
题意
就是给你一颗n个点的无根树,每个点都有一个货物,都有价格。
然后给你m个询问,每个询问问你从x->y的路径中你有序的选两个货物,ai,aj
使得ai-aj最大。注意选的时候一定要有顺序。
分析
题目是关于树的,我们可以发现,从x->y的路径中一定会经过它们的最近公共祖先。
所以我们可以分类讨论,有三种情况:
1.在x->lca(x,y)中完成买和卖
2.在lca(x,y)->y中完成买和卖
3.在x->lca(x,y)中买了,在lca(x,y)->y中卖出
其中第三种情况比较简单,只需要算出两个rmq一个最大,一个最小
然后在x->lca(x,y)里面算出最小值,在lca(x,y)->y里面算出最大值即可。
但是第一第二种情况我们不能这样算的原因是我们不能保证它们的先后顺序。
难道这样就是错的,不行,我们继续思考。
能不能也用一个rmq数组去统计这一段的先卖后买的答案呢?
我们发现这样是可以的:
我们设g[i,j]表示第i个节点往上
2j
个节点的编号。
f[i,j]表示第i个节点往上
2j
个节点中的最小值
q[i,j]表示第i个节点往上
2j
个节点中的最大值
u[i,j]表示第i个节点往上
2j
个节点中的先买后卖的最大值。
g[i,j]=g[g[i,j-1],j-1];
f[i,j]=min(f[g[i,j-1],j-1],f[i,j-1]);
q[i,j]=max(q[g[i,j-1],j-1],q[i,j-1]);
u[i,j]=max(u[i,j-1],u[g[i,j-1],j-1],f[i,j-1]+q[g[i,j-1,j-1]);
这样我们就可以从x跳到lca(x,y),和lca(x,y)跳到y的路径中计算答案。
计算答案:
1.在x->lca(x,y)中完成买和卖:
我们不仅每次取跳上去时u的答案,还要每次记录前面的最小值,然后和后面的最大值的差作比较。
2.在lca(x,y)->y中完成买和卖:同理这里也是
3.在x->lca(x,y)中买了,在lca(x,y)->y中卖出:这里就直接取最大值最小值作差即可。
代码
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#define maxlongint 2147483647
using namespace std;
const int N=50005;
int nu,n,b[N*2],las[N],nex[N*2],m,x,y,val[N],g[N][20],f[N][20],q[N][20],u[N][20][2],d[N],fa[N];
void insert(int x,int y){
b[++nu]=y;nex[nu]=las[x];las[x]=nu;
}
void dfs(int x,int y){
for(int p=las[x];p;p=nex[p]){
if (b[p]!=y) {
d[b[p]]=d[x]+1;
g[b[p]][0]=x;
fa[b[p]]=x;
f[b[p]][0]=min(val[b[p]],val[x]);
q[b[p]][0]=max(val[b[p]],val[x]);
u[b[p]][0][1]=max(0,val[b[p]]-val[x]);
u[b[p]][0][0]=max(0,val[x]-val[b[p]]);
dfs(b[p],x);
}
}
}
int lca(int x,int y){
if (d[x]<d[y]) swap(x,y);
int k=trunc(log(d[x]-d[y]+1)/log(2));
while (k>=0){
if (d[g[x][k]]>d[y]) x=g[x][k];
--k;
}
if (d[x]!=d[y]) x=g[x][0];
k=trunc(log(d[x])/log(2));
while (k>=0){
if (g[x][k]!=g[y][k]) x=g[x][k],y=g[y][k];
k--;
}
if (x==y) return x;else return g[x][0];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&val[i]);
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
insert(x,y);
insert(y,x);
}
d[1]=1;
dfs(1,0);
for(int j=1;j<=trunc(log(n)/log(2));j++){
x=x;
for(int i=1;i<=n;i++){
g[i][j]=g[g[i][j-1]][j-1];
f[i][j]=min(f[i][j-1],f[g[i][j-1]][j-1]);
q[i][j]=max(q[i][j-1],q[g[i][j-1]][j-1]);
u[i][j][0]=max(u[i][j-1][0],u[g[i][j-1]][j-1][0]);
u[i][j][1]=max(u[i][j-1][1],u[g[i][j-1]][j-1][1]);
u[i][j][0]=max(u[i][j][0],q[g[i][j-1]][j-1]-f[i][j-1]);
u[i][j][1]=max(u[i][j][1],q[i][j-1]-f[g[i][j-1]][j-1]);
}
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
int z=lca(x,y);
int X=x,Y=0,An=maxlongint,bn=0,ans=0;
int k=trunc(log(d[x]-d[z]+1)/log(2));
bool p=0;
while (k>=0){
if (d[g[X][k]]>=d[z]) {
ans=max(q[X][k]-An,ans);
if (!p) {Y=k;p=1;}else bn=max(bn,q[X][k]);
An=min(An,f[X][k]);
ans=max(ans,u[X][k][0]);X=g[X][k];
}--k;
}
ans=max(ans,bn-f[x][Y]);
Y=y;
k=trunc(log(d[Y]-d[z]+1)/log(2)),bn=maxlongint,X=0;
int Cn=0;
p=0;
while (k>=0){
if (d[g[Y][k]]>=d[z]) {
ans=max(Cn-f[Y][k],ans);
if (!p) {X=k;p=1;}else bn=min(bn,f[Y][k]);
Cn=max(Cn,q[Y][k]);
ans=max(ans,u[Y][k][1]);Y=g[Y][k];
}--k;
}
ans=max(max(ans,Cn-An),ans);
if (p) ans=max(ans,q[y][X]-bn);
printf("%d\n",ans);
}
}