提高培优班D10T3行商题解

提高培优班套题训练10T3题解

题意简述

给定一棵有 n 个带点权节点的树,求从 x 到 y 的路径上的最大收益(不能回溯)。

思路

首先,我们考虑暴力。

直接从 x 暴力跳到 lca 并维护当前最小值,同时计算当前值与最小值的差取 max 。

同理,y做反向操作。最后再把两边维护的 max-min 和 ans 取 max ,就能得到最终答案。

没错这样写就能得到100分的好成绩

在暴力过程中,我们发现,时间复杂度都叠在遍历整个链,那怎么减少这个复杂度呢?

我们可以考虑类似于倍增跳 lca 的方式去计算。

那我们要维护的东西也就推出来了:

  • f_{i,j} 表示 i 节点向上跳 2^j 步的节点编号(找 lca)
  • ma_{i,j} 表示 i 节点向上跳 2^j 步的最大点权
  • mi_{i,j} 表示 i 节点向上跳 2^j 步的最小点权(暴力的时候,维护的当前最小、最大)
  • retup_{i,j} 表示 i 节点向上跳 2^j 步向上走的贡献(即从 x 出发的贡献)
  • retdown_{i,j} 表示 i 节点向上跳 2^j 步向下走的贡献(即从 y 出发的贡献)

那么难点应该就集中在 retup 和 retdown 的转移上。

以 retup 为例,

假设,我们 x 已经跳到新的位置,并计算好当前我们所需维护的东西,并且下一步就要跳到 lca 了(显然其他位置也是同理的)。

那么 f_{x,j}=lca

令 t 为 lca 和 x 的中点,即 f_{x,j-1}=t

那么,答案一共有三种情况:

  • 取的两点都在 x\to t 的链上,贡献就是 retup_{x,j-1}
  • 取的两点都在 t\to lca 的链的链上,贡献就是 retup_{t,j-1}
  • 取的两点分别在$x\to t$ 和$t\to lca$ 的链上,贡献就是 ma_{t,j-1}-mi_{x,j-1}

这三种直接取 max 就好了。

最终计算答案和 retdown 也是相似的情况,就不多赘述了,详见代码吧。

code

int n,m,s;
vector<int>e[N];
int f[20][N],dep[N],a[N];
int ma[20][N],mi[20][N],retdown[20][N],retup[20][N];
inline void dfs(int u,int fa){
	dep[u]=dep[fa]+1;f[0][u]=fa;
    ma[0][u]=max(a[u],a[fa]);mi[0][u]=min(a[u],fa==0?0x7fffffff:a[fa]);
	retdown[0][u]=a[u]-(fa==0?0x7fffffff:a[fa]);retup[0][u]=a[fa]-a[u];
	for(int a=1;a<=19;++a) f[a][u]=f[a-1][f[a-1][u]],ma[a][u]=max(ma[a-1][u],ma[a-1][f[a-1][u]]),
		mi[a][u]=min(mi[a-1][u],mi[a-1][f[a-1][u]]),
		retdown[a][u]=max(ma[a-1][u]-mi[a-1][f[a-1][u]],max(retdown[a-1][u],retdown[a-1][f[a-1][u]])),
		retup[a][u]=max(ma[a-1][f[a-1][u]]-mi[a-1][u],max(retup[a-1][u],retup[a-1][f[a-1][u]]));
	for(const int &v:e[u]){
		if(v==fa)continue;
		dfs(v,u);
	}
}
inline int lca(int x,int y){
	if(dep[x]<dep[y])x^=y^=x^=y;
	for(int a=19;~a;--a)
		if(dep[f[a][x]]>=dep[y]) x=f[a][x];
	if(x==y) return x;
	for(int a=19;~a;--a)
		if(f[a][x]^f[a][y])
			x=f[a][x],y=f[a][y];
	return f[0][x];
}
signed main(){
	read(n);
	for(int i=1;i<=n;i++)read(a[i]);
	for(int i=1,x,y;i<n;i++) {
		read(x,y);
		e[x].emplace_back(y),e[y].emplace_back(x);
	}
	read(m);
	dfs(1,0);
	int x,y,LCA,ans,max_,min_,X,Y;
	while(m--){
		read(x,y);LCA=lca(x,y);ans=0;max_=-1;min_=0x7fffffff;X=x,Y=y;
		if(x==y){puts("0");continue;}
		if(x!=LCA){
			for(int a=19;~a;--a){
				if(dep[f[a][x]]>=dep[LCA]){
					ans=max(max(ma[a][x]-min_,retup[a][x]),ans);
//					printf("(ma[a][x]:%d min:%d)\n",ma[a][x],min_);
//					printf("retup:%d a:%d x:%d\n",retup[a][x],a,x);
					min_=min(min_,mi[a][x]);
					x=f[a][x];
				}
			}
		//	printf("<1:%d>\n",ans);
		}
		if(y!=LCA){
			for(int a=19;~a;--a){
				if(dep[f[a][y]]>=dep[LCA]){
					ans=max(max(max_-mi[a][y],retdown[a][y]),ans);
//					printf("(mi[a][x]:%d max:%d)\n",mi[a][x],max_);
//					printf("retdown:%d a:%d y:%d\n",retdown[a][y],a,y);
					max_=max(max_,ma[a][y]);
					y=f[a][y];
				}
			}	
		//	printf("<2:%d>\n",ans);
		}
		if(X!=LCA&&Y!=LCA){
			ans=max(ans,max_-min_);
		//	printf("<3:%d>\n",ans);
		}
		printf("%d\n",ans);
	}
    return 0;
}