题意:给出一棵带点权的树,给出Q个询问(a,b),问从a到b的最大盈利(即:先在最小值买入,再在最大值卖出)。要求只能买入一次卖出一次,且买入在卖出之前。如果不能收益正值,则输出0。
思路:分析参考了(http://blog.csdn.net/xingyeyongheng/article/details/20402603)
对于每一对查询(u,v),先求出点u,v的最近公共祖先f,然后求u->f->v的利润最大值maxval
对于这个maxval可能有三种情况:
1:maxval是u->f的maxval
2:maxval是f->v的maxval
3:maxval是f->v的最大w[i]减去u->f的最小w[i]
分析到这很明显需要设置4个变量来求maxval:
up[u]表示u->f的最大maxval
down[u]表示f->u的最大maxval
maxw[u]表示u-f的最大w[i]
minw[u]表示u-f的最小w[i]
所以maxval=max(max(up[u],down[v]),maxw[v]-minw[u]);
现在问题就是如何快速的求出这四个变量,在这里我们可以对u,v的LCA(u,v)进行分类解决
对于LCA(u,v)是f的询问全部求出,然后再求LCA(u,v)是f的父亲的询问
这样当我们求LCA(u,v)是f的父亲的询问的时候就可以借用已经求出的LCA(u,v)是f的询问
的结果,这样就不用反复去求u->f的那四个变量值,u->father[f]也能快速求出
这个变化主要在寻找father[v]这个过程中进行,具体看代码
#include <cstdio> #include <queue> #include <algorithm> #include <cstring> using namespace std; #define INF 0x3fffffff #define clr(s,t) memset(s,t,sizeof(s)); #define N 50005 int f1[N],f2[N],f3[N],top1,top2,top3; struct edge{ int y,next,id; }e1[N<<1],e2[N<<1],e3[N]; struct question{ int x,y,res; }q[N]; int n,m; int up[N],down[N],minv[N],maxv[N]; int used[N],root[N]; void add1(int x,int y){ e1[top1].y = y; e1[top1].next = f1[x]; f1[x] = top1++; } void add2(int x,int y,int id){ e2[top2].y = y; e2[top2].id = id; e2[top2].next = f2[x]; f2[x] = top2++; } void add3(int x,int y,int id){ e3[top3].y = y; e3[top3].id = id; e3[top3].next = f3[x]; f3[x] = top3++; } int find(int x){ int fa; if(x == root[x]) return x; fa = root[x]; root[x] = find(root[x]); up[x] = max(max(up[x],up[fa]),maxv[fa]-minv[x]); down[x] = max(max(down[x],down[fa]),maxv[x]-minv[fa]); minv[x] = min(minv[x],minv[fa]); maxv[x] = max(maxv[x],maxv[fa]); return root[x]; } void solve(int x){ int i,j,a,b; used[x] = 1; root[x] = x; for(i = f2[x];i!=-1;i=e2[i].next){ if(!used[e2[i].y]) continue; j = find(e2[i].y); add3(j,e2[i].y,e2[i].id);//记录一下这个查询的LCA,此时已经能够求出来了就是j } for(i = f1[x];i!=-1;i=e1[i].next){ if(used[e1[i].y]) continue; solve(e1[i].y); root[e1[i].y] = x; } for(i = f3[x];i!=-1;i=e3[i].next){//求查询的LCA是x的情形 j = e3[i].id; a = q[j].x; b = q[j].y; find(a); find(b); q[j].res = max(max(up[a],down[b]),maxv[b]-minv[a]); } } int main(){ int i,j,a,b; scanf("%d",&n); for(i = 1;i<=n;i++) f1[i] = f2[i] = f3[i] = -1; top1 = top2 = top3 = 0; clr(used, 0); for(i = 1;i<=n;i++){ scanf("%d",&j); minv[i] = maxv[i] = j; up[i] = down[i] = 0; } for(i = 1;i<n;i++){ scanf("%d %d",&a,&b); add1(a,b); add1(b,a); } scanf("%d",&m); for(i = 1;i<=m;i++){ scanf("%d %d",&q[i].x,&q[i].y); add2(q[i].x,q[i].y,i); add2(q[i].y,q[i].x,i); } solve(1); for(i = 1;i<=m;i++) printf("%d\n",q[i].res); return 0; }