poj3728 The merchant 倍增LCA

来源:互联网 发布:南风知我意txt下载 编辑:程序博客网 时间:2024/05/16 07:48

给定一个点带权的树,有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 secondusing namespace std;#define N 50005int 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;}

原创粉丝点击