【POJ 3728】The merchant

黑恶势力向你招手~~
可以很快看出是LCA的题,但会发现此题涉及位置,不是简单的max-min。
可以发现分三种情况讨论:在u—LCA中买并卖;在LCA—v中买并卖;在u—LCA中买,在LCA—v中卖。
第一种情况需要min离u较近;第二种情况需要min离LCA较近;第三种就简单用max与min就行了。
然后,可用倍增求出。(好神奇)
上码!

#include<cstdio>
#include<vector>
using namespace std;
const int N = 50005, Inf = (1 << 30) - 1;
int n, w[N], T, d[N], dis[N][16], Limit, max1[N][16], min1[N][16], up[N][16], down[N][16];
vector <int> G[N];
void read(int &x) {
 x = 0;
 char s = getchar();
 while(s > '9' || s < '0')
  s = getchar();
 while(s <= '9' && s >= '0') {
  x = (x << 1) + (x << 3) + s - '0';
  s = getchar();
 }
}
int Log2(const float &x) {
 return ((unsigned &) x >> 23 & 255) - 127;
} 
int Max(const int x, const int y) {
 return x > y ? x : y;
}
int Min(const int x, const int y) {
 return x < y ? x : y; 
}
void init(const int x, const int ba) {
 d[x] = d[ba] + 1;
 dis[x][0] = ba;
 max1[x][0] = Max(w[x], w[ba]);//A
 min1[x][0] = Min(w[x], w[ba]);
 up[x][0] = Max(0, w[ba] - w[x]);
 down[x][0] = Max(0, w[x] - w[ba]);
 for(int i = 1; (1 << i) <= d[x]; i ++) {
  dis[x][i] = dis[dis[x][i - 1]][i - 1];
  max1[x][i] = Max(max1[x][i - 1], max1[dis[x][i - 1]][i - 1]);//B
  min1[x][i] = Min(min1[x][i - 1], min1[dis[x][i - 1]][i - 1]);
  up[x][i] = Max(Max(up[x][i - 1], up[dis[x][i - 1]][i - 1]), max1[dis[x][i - 1]][i - 1] - min1[x][i - 1]);
  down[x][i] = Max(Max(down[x][i - 1], down[dis[x][i - 1]][i - 1]), max1[x][i - 1] - min1[dis[x][i - 1]][i - 1]);
 }
 for(int i = 0; i < G[x].size(); i ++) {
  if(ba == G[x][i])
   continue;
  init(G[x][i], x);
 }
}
void Swap(int &x, int &y) {
 int t = x;
 x = y;
 y = t;
}
int lca(int x, int y) {
 if(d[x] > d[y])
  Swap(x, y);
 int gap = d[y] - d[x];
 for(int i = 0; (1 << i) <= gap; i ++)
  if((1 << i) & gap)
   y = dis[y][i];
 if(x == y)
  return x;
 for(int i = Limit; i >= 0; i --)
  if(dis[x][i] != dis[y][i]) {
   x = dis[x][i];
   y = dis[y][i];
  }
 return dis[x][0];
}
int Aup(int x, const int gap, int &mi) {
 int res = 0;
 mi = Inf;
 for(int i = 0; (1 << i) <= gap; i ++)
  if((1 << i) & gap) {
   res = Max(res, Max(max1[x][i] - mi, up[x][i]));//C
   mi = Min(mi, min1[x][i]);
   x = dis[x][i];
  }
 return res;
}
int Adown(int x, const int gap, int &ma) {
 int res = 0;
 ma = 0;
 for(int i = 0; (1 << i) <= gap; i ++)
  if((1 << i) & gap) {
   res = Max(res, Max(ma - min1[x][i], down[x][i]));
   ma = Max(ma, max1[x][i]);
   x = dis[x][i];
  }
 return res;
}
int main() {
 int a, b, LCA, q, p;
 read(n);
 Limit = Log2(n);
 for(int i = 1; i <= n; i ++)
  read(w[i]);
 for(int i = 1; i < n; i ++) {
  read(a);
  read(b);
  G[a].push_back(b);
  G[b].push_back(a);
 }
 init(1, 0);
 read(T);
 while(T --) {
  read(a);
  read(b);
  LCA = lca(a, b);
  printf("%d\n", Max(Max(Aup(a, d[a] - d[LCA], q), Adown(b, d[b] - d[LCA], p)), p - q));
 }
 return 0;
}

这题没什么坑,唯一的就是——不要输出负数哇咔咔!

A:因为表示u至其父节点,所以直接赋值。
B:我理解的是二分,这样就不难理解了。至于up与down,除了二分,还要思考另一种情况(在前一段买,在后一段卖)。
C:res在mi的前面,因为要先买才能卖嘛。而此时便表示这一段最大减去之前最小与这一段本身的最值比较。(Adown函数同理)

如果有不对的地方,请大佬在评论区指出哦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值