POJ 3728 The merchant (LCA DP)

POJ 3728 The merchant (LCA DP)

POJ - 3728

题目大意:给出一棵树,每次询问给出 u , v u,v u,v 两点,在 u u u v v v 的有向路径上先后选出两个点 u ′ , v ′ u',v' u,v ,令 w ( v ′ ) − w ( u ′ ) w(v')-w(u') w(v)w(u) 最大。

可以想到三种情况:

  1. u → l c a ( u , v ) u\rightarrow {\rm lca}(u,v) ulca(u,v) 的路径上买卖
  2. l c a ( u , v ) → v {\rm lca}(u,v)\rightarrow v lca(u,v)v 的路径上买卖
  3. u → l c a ( u , v ) u\rightarrow {\rm lca}(u,v) ulca(u,v) 的路径上买入,在 l c a ( u , v ) → v {\rm lca} (u,v)\rightarrow v lca(u,v)v 的路径上卖出

第三种情况容易处理,可以暴力树剖 + 线段树求出两条路径上的最小权值和最大权值,然后求得答案。

但是第一种和第二种情况不好处理,问题在于选出的 u ′ , v ′ u',v' u,v 有先后顺序。

这里给出倍增求 LCA + DP 的方法。

在倍增求 LCA 的时候,我们可以顺带求出一些值,方便我们后面计算答案。

f a ( u , i ) {\rm fa}(u,i) fa(u,i) 表示 u u u 2 i 2^i 2i 级祖先,那么我们可以求出 u u u f a ( u , i ) {\rm fa}(u,i) fa(u,i) 的一下参数:

  1. f u p ( u , i ) f_{\rm up}(u,i) fup(u,i) 表示 u → f a ( u , i ) u\rightarrow {\rm fa}(u,i) ufa(u,i) 上的最大收益
  2. f d o w n ( u , i ) f_{\rm down}(u,i) fdown(u,i) 表示 f a ( u , i ) → u {\rm fa}(u,i)\rightarrow u fa(u,i)u 上的最大收益
  3. f max ⁡ ( u , i ) f_{\max}(u,i) fmax(u,i) 表示 u u u f a ( u , i ) {\rm fa}(u,i) fa(u,i) 之间的最大权值
  4. f min ⁡ ( u , i ) f_{\min}(u,i) fmin(u,i) 表示 u u u f a ( u , i ) {\rm fa}(u,i) fa(u,i) 之间的最小权值

对于参数 3 , 4 ,我们可以这样求:
f max ⁡ ( u , i ) = max ⁡ { f max ⁡ ( u , i − 1 ) , f max ⁡ ( f a ( u , i − 1 ) , i − 1 ) } f min ⁡ ( u , i ) = min ⁡ { f min ⁡ ( u , i − 1 ) , f min ⁡ ( f a ( u , i − 1 ) , i − 1 ) } \begin{aligned} f_{\max}(u,i)&=\max\{f_{\max}( u,i-1),f_{\max}({\rm fa}(u,i-1),i-1)\}\\[2ex] f_{\min}(u,i)&=\min\{f_{\min}(u,i-1),f_{\min}({\rm fa}(u,i-1),i-1)\} \end{aligned} fmax(u,i)fmin(u,i)=max{fmax(u,i1),fmax(fa(u,i1),i1)}=min{fmin(u,i1),fmin(fa(u,i1),i1)}
因为在求 f a ( u , i ) {\rm fa}(u,i) fa(u,i) 的时候, f a ( u , i ) = f a ( f a ( i − 1 ) , i − 1 ) {\rm fa}(u,i) = {\rm fa}({\rm fa}(i - 1),i-1) fa(u,i)=fa(fa(i1),i1) ,所以 u u u f a ( u , i − 1 ) {\rm fa}(u,i-1) fa(u,i1) 之间的最值与 f a ( u , i − 1 ) {\rm fa}(u,i - 1) fa(u,i1) f a ( u , i ) {\rm fa}(u,i) fa(u,i) 之间的最值对 u u u f a ( u , i ) {\rm fa}(u,i) fa(u,i) 之间的最值有贡献。

同理,我们可以得出参数 1 , 2 的方程,只不过要注意,除了 u u u f a ( u , i − 1 ) {\rm fa}(u,i-1) fa(u,i1) 之间的贡献与 f a ( u , i − 1 ) {\rm fa}(u,i - 1) fa(u,i1) f a ( u , i ) {\rm fa}(u,i) fa(u,i) 之间的贡献,还要考虑直接跨过 f a ( u , i − 1 ) {\rm fa}(u,i-1) fa(u,i1) 整段区间的最值 f max ⁡ − f min ⁡ f_{\max} - f_{\min} fmaxfmin
f u p ( u , i ) = max ⁡ { f u p ( u , i − 1 ) , f u p ( f a ( u , i − 1 ) , i − 1 ) , f max ⁡ ( f a ( u , i − 1 ) , i − 1 ) − f min ⁡ ( u , i − 1 ) } f d o w n ( u , i ) = max ⁡ { f d o w n ( u , i − 1 ) , f d o w n ( f a ( u , i − 1 ) , i − 1 ) , f max ⁡ ( u , i − 1 ) − f min ⁡ ( f a ( u , i − 1 ) , i − 1 ) } \begin{aligned} f_{\rm up}(u,i) &= \max\{f_{\rm up}(u,i-1),f_{\rm up}({\rm fa}(u,i-1),i-1),f_{\max}({\rm fa}(u,i-1),i-1)-f_{\min}(u,i-1)\}\\[2ex] f_{\rm down}(u,i) &= \max\{f_{\rm down}(u,i-1),f_{\rm down}({\rm fa}(u,i-1),i-1),f_{\max}(u,i-1)-f_{\min}({\rm fa}(u,i-1),i-1)\} \end{aligned} fup(u,i)fdown(u,i)=max{fup(u,i1),fup(fa(u,i1),i1),fmax(fa(u,i1),i1)fmin(u,i1)}=max{fdown(u,i1),fdown(fa(u,i1),i1),fmax(u,i1)fmin(fa(u,i1),i1)}

但是这仅仅是预处理部分,处理这些参数有助于我们待会儿 DP.

一次询问给出 u , v u,v u,v ,那么自然要求其 LCA ,上面我们已经预处理了一些信息,然后考虑利用这些信息在倍增求 LCA 的过程中计算答案。

处理 u → l c a ( u , v ) u\rightarrow {\rm lca}(u,v) ulca(u,v) 的过程:

在向上跳的过程中,要求最大收益,那么每一次向上跳都可以得知其中一段的最大权值,于是我们只要记录一个当前最小权值,然后在跳的时候更新一下答案,之后再更新这个最小权值,如此反复就可以求得 u → l c a u\rightarrow {\rm lca} ulca 过程中的最大收益。

在这里插入图片描述

在跳的过程中,保持更新
a n s = max ⁡ { f max ⁡ ( u , i ) − m , a n s } m = min ⁡ { f min ⁡ ( u , i ) , m } {\rm ans} = \max\{f_{\max}(u,i)-m,{\rm ans}\}\\ m = \min\{f_{\min}(u,i),m\} ans=max{fmax(u,i)m,ans}m=min{fmin(u,i),m}
同理,处理 l c a ( u , v ) → v {\rm lca}(u,v)\rightarrow v lca(u,v)v 的过程也是类似的
a n s = max ⁡ { m x − f min ⁡ ( u , i ) , a n s } m x = max ⁡ { f max ⁡ ( u , i ) , m x } {\rm ans} = \max\{mx-f_{\min}(u,i),{\rm ans}\}\\ mx = \max\{f_{\max}(u,i),mx\} ans=max{mxfmin(u,i),ans}mx=max{fmax(u,i),mx}
最后跳到 l c a ( u , v ) {\rm lca}(u,v) lca(u,v) 的时候,我们会发现先前记录的 m m m m x mx mx 恰好就是结点到 LCA 链上的最小值和最大值,于是再更新一次答案 a n s = max ⁡ { a n s , m x − m } {\rm ans} = \max\{{\rm ans},mx-m\} ans=max{ans,mxm} 就计算出答案了。

预处理时间复杂度 O ( N ) O(N) O(N) ,回答询问的时间复杂度 O ( Q log ⁡ N ) O(Q\log N) O(QlogN)

启示:DP 可以加载到倍增求 LCA 上,用于减小时间复杂度。树上的最值问题要考虑运用 DP.

void dfs(int u,int pre)
{
	dep[u] = dep[pre] + 1;
	for(int i = 1;i <= 25;i ++)
	{
		fa[u][i] = fa[fa[u][i - 1]][i - 1];
		maxf[u][i] = max(maxf[fa[u][i - 1]][i - 1],max(w[u],maxf[u][i - 1]));//从 u 到 fa(u,i) 路径上最大权值 
		minf[u][i] = min(minf[fa[u][i - 1]][i - 1],min(w[u],minf[u][i - 1]));//从 u 到 fa(u,i) 路径上最小权值 
		upf[u][i] = max(max(upf[fa[u][i - 1]][i - 1],upf[u][i - 1]),maxf[fa[u][i - 1]][i - 1] - minf[u][i - 1]);//从 u 向上先买入后卖出的最大收益 
		downf[u][i] = max(max(downf[fa[u][i - 1]][i - 1],downf[u][i - 1]),maxf[u][i - 1] - minf[fa[u][i - 1]][i - 1]);//从 fa(u,i) 向下先买入后卖出的最大收益 
	}
	for(int i = head[u],v = 0;i;i = e[i].nxt)
	{
		v = e[i].to;
		if(v == pre) continue;
		fa[v][0] = u;
		maxf[v][0] = max(w[u],w[v]);
		minf[v][0] = min(w[u],w[v]);
		upf[v][0] = max(0,w[u] - w[v]);
		downf[v][0] = max(0,w[v] - w[u]);
		dfs(v,u);
	}
	return ;
}

int solve(int u,int v)
{
	int tmpx = u;int tmpy = v;int res = 0;
	int maxup = w[v];int mindown = w[u];
	if(dep[tmpx] < dep[tmpy])
	{
		for(int i = 25;i >= 0;i --)
		{
			if(dep[fa[tmpy][i]] >= dep[tmpx])
			{
				res = max(res,maxup - minf[tmpy][i]);
				maxup = max(maxup,maxf[tmpy][i]);
				res = max(res,downf[tmpy][i]);//
				tmpy = fa[tmpy][i];
			}
		}
	}
	else
	{
		for(int i = 25;i >= 0;i --)
		{
			if(dep[fa[tmpx][i]] >= dep[tmpy])
			{
				res = max(res,maxf[tmpx][i] - mindown);
				mindown = min(mindown,minf[tmpx][i]);
				res = max(res,upf[tmpx][i]);//
				tmpx = fa[tmpx][i];
			}
		}
	}
	if(tmpx == tmpy) return res;
	for(int i = 25;i >= 0;i --)
	{
		if(fa[tmpx][i] != fa[tmpy][i])
		{
			res = max(res,maxf[tmpx][i] - mindown);
			res = max(res,maxup - minf[tmpy][i]);
			maxup = max(maxup,maxf[tmpy][i]);
			mindown = min(mindown,minf[tmpx][i]);
			res = max(res,max(upf[tmpx][i],downf[tmpy][i]));
			tmpx = fa[tmpx][i];
			tmpy = fa[tmpy][i];
		}
	}
	res = max(res,maxf[tmpx][0] - mindown);
	res = max(res,maxup - minf[tmpy][0]);
	maxup = max(maxup,maxf[tmpy][0]);mindown = min(mindown,minf[tmpx][0]);
	res = max(res,maxup - mindown);
	return res;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 给出一个$n\times m$的矩阵,每个位置上有一个非负整数,代表这个位置的海拔高度。一开始时,有一个人站在其中一个位置上。这个人可以向上、下、左、右四个方向移动,但是只能移动到海拔高度比当前位置低或者相等的位置上。一次移动只能移动一个单位长度。定义一个位置为“山顶”,当且仅当从这个位置开始移动,可以一直走到海拔高度比它低的位置上。请问,这个矩阵中最多有多少个“山顶”? 输入格式 第一行两个整数,分别表示$n$和$m$。 接下来$n$行,每行$m$个整数,表示整个矩阵。 输出格式 输出一个整数,表示最多有多少个“山顶”。 样例输入 4 4 3 2 1 4 2 3 4 3 5 6 7 8 4 5 6 7 样例输出 5 算法1 (递归dp) $O(nm)$ 对于这道题,我们可以使用递归DP来解决,用$f(i,j)$表示以$(i,j)$为起点的路径最大长度,那么最后的答案就是所有$f(i,j)$中的最大值。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码 算法2 (动态规划) $O(nm)$ 动态规划的思路与递归DP类似,只不过转移方程和实现方式有所不同。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值