Road

题目

这里写图片描述
数据约定:
对于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);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值