poj3728 The merchant 倍增LCA

给定一个点带权的树,有Q个询问。

一次询问求从X到Y的这条路径上,从一个点买从这个点之后的某个点卖,求最大收益,买必须再卖之前。


求倍增LCA的时候维护5个值

f  ij : i的2^j级祖先

fm ij i到i的2^j级祖先的最大点权

fn ij i到i的2^j级祖先的最小点权

sm ij i到i的2^j级祖先的最大收益

sn ij i到i的2^j级祖先的最小收益


从x到y就看成了从x向上到LCA(x,y)向下到y,两条路径分开考虑最后再合并答案即可。

为什么要维护最小收益呢,因为从LCA(x,y)到y的过程就相当于是把从y到LCA(x,y)倒过来看,LCA(x,y)到y的最大值就是y到LCA(x,y)的最小值。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <queue>
#include <stack>
#include <utility>
#include <map>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
#define N 50005

int n , m , pre[N] , mcnt , Q;
struct edge
{
  int x , next;
}e[N << 1];
int d[N] , f[N][16] , a[N];
int fm[N][16] , fn[N][16] , sm[N][16] , sn[N][16] ;

void dfs(int x , int fa , int dep)
{
  d[x] = dep , f[x][0] = fa;
  for (int i = pre[x] ; ~i ; i = e[i].next)
    if (fa != e[i].x)
      dfs(e[i].x , x , dep + 1);
}

int query(int x , int y)
{
  int i , xx = 0 , yy = 0 , X = a[x] , Y = a[y];
  i = 15;
  while (d[x] != d[y])
  {
    if (abs(d[x] - d[y]) >= 1 << i)
    {
      if (d[y] < d[x])
        xx = max( max(xx , sm[x][i]) , fm[x][i] - X) , X = min(X , fn[x][i]) , x = f[x][i];
      else yy = min( min(yy , sn[y][i]) , fn[y][i] - Y) , Y = max(Y , fm[y][i]) , y = f[y][i];
    }
    -- i;
  }
  if (x == y)
    return max( max(xx , -yy) , Y - X );
  i = 15;
  while (i >= 0)
  {
    if (f[x][i] && f[y][i] && f[x][i] != f[y][i])
    {
      xx = max( max(xx , sm[x][i]) , fm[x][i] - X) , X = min(X , fn[x][i]) , x = f[x][i];
      yy = min( min(yy , sn[y][i]) , fn[y][i] - Y) , Y = max(Y , fm[y][i]) , y = f[y][i];
    }
    -- i;
  }
  i = 0;
  xx = max( max(xx , sm[x][i]) , fm[x][i] - X) , X = min(X , fn[x][i]) , x = f[x][i];
  yy = min( min(yy , sn[y][i]) , fn[y][i] - Y) , Y = max(Y , fm[y][i]) , y = f[y][i];
  return max( max(xx , -yy) , Y - X );
}

void work()
{
  int i , j , x , y;
  scanf("%d",&n);
  for (i = 1 ; i <= n ; ++ i)
    scanf("%d",&a[i]);
  memset(pre , -1 , sizeof(pre));
  for (i = 1 ; i < n ; ++ i)
  {
    scanf("%d%d",&x,&y);
    e[mcnt] = (edge) {y , pre[x]} , pre[x] = mcnt ++;
    e[mcnt] = (edge) {x , pre[y]} , pre[y] = mcnt ++;
  }
  dfs(1 , 0 , 0);
  fm[1][0] = fn[1][0] = a[1] , sm[1][0] = -1 << 30 , sn[1][0] = 1 << 30;
  for (i = 2 ; i <= n ; ++ i)
  {
    fm[i][0] = max(a[i] , a[f[i][0]]);
    fn[i][0] = min(a[i] , a[f[i][0]]);
    sm[i][0] = max(-a[i] + a[f[i][0]] , 0) , sn[i][0] = min(-a[i] + a[f[i][0]] , 0);
  }
  for (j = 1 ; 1 << j < n ; ++ j)
    for (i = 1 ; i <= n ; ++ i)
    {
      x = f[i][j - 1];
      f[i][j] = f[x][j - 1];
      fm[i][j] = max(fm[i][j - 1] , fm[x][j - 1]);
      fn[i][j] = min(fn[i][j - 1] , fn[x][j - 1]);
      sm[i][j] = max( max(sm[i][j - 1] , sm[x][j - 1])  , fm[x][j - 1] - fn[i][j - 1]);
      sn[i][j] = min( min(sn[i][j - 1] , sn[x][j - 1])  , fn[x][j - 1] - fm[i][j - 1]);
    }
  scanf("%d",&Q);
  while (Q --)
  {
    scanf("%d%d",&x,&y);
    printf("%d\n" , query(x , y));
  }
}

int main()
{
  work();
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值