CF1648D
题面
给你 \(3\times n\) 的矩阵, \(i\) 行 \(j\) 列的权值为 \(a_{i,j}\) ,你需要从 \((1,1)\) 只向右、向下走到 \((3,n)\) 。
最开始第 \(2\) 行所有点都不能走,有 \(m\) 个解锁方法,形如 \(l_i,r_i,z_i\) ,表示将 \(a_{2,l_i}\sim a_{2,r_i}\) 解锁需要花费 \(z_i\) 的代价(你可以选多个解锁方法。)。
一条路径的价值为路径中所有点的权值之和 \(-\) 解锁花费的代价之和,求权值最大的路径权值大小。
数据范围:\(n,m\le 5\times 10^5\) 。
题解
很有启发的一个题,话说这为什么是 *2800
啊...
- naive的想法
一开始肯定会想确定到第 \(2\) 行的起点 \(l\) ,终点 \(r\) ,然后这样一条路径的权值就是 \(su_{1,l}+su_{2,r}-su_{2,l-1}+ne_{3,r}-cost(l,r)\) ,其中 \(cost(i,j)\) 表示将 \(a_{2,l}\sim a_{2,r}\) 都解锁需要花费的代价。
然后可能会考虑分治,对于 \(mid\) 考虑所有会经过 \(mid\) 的方案,这样选前缀中最大的和后缀中最大的就是答案。
但是因为有 \(cost(i,j)\) ,前缀和后缀的答案是无法合并的。
怎么办,好像G了。
- 可以DP
发现走到了那里是可以记录为状态的,那么我们设 \(f_i\) 表示走到了 \((2,i)\) 的最大权值,就有转移:
这和上面的有区别吗?
有!因为我们DP是一步一步来的,所以这里的 \(cost(i,j)\) 就可以表示为所有解锁区间 可以覆盖 \((2,i)\sim(2,j)\) 的代价最小值。
但这转移还是 \(n^2\) 的,还不包括后面求 \(cost(i,j)\) ,怎么办?
- cdq优化dp
第一次发现这么好的cdq优化dp题。
基本的分治,先处理左边不用多说,现在我们假设 \([l,mid]\) 的所有值已经求出来了。
对于一个跨过 \(mid\) 的解锁方法 \(l_i,r_i,z_i\),我们可以给满足:\(\max(l_i,l)\le i\le mid<j\le \min(r,r_i)\) 的 \((i,j)\) 作上面的转移。
然后就可以给 \(mid\) 前的用后缀 \(\max\) 得到最大值,转移之后再用向前去更新。
但是这样复杂度还是 \(nm\) 的(每个解锁方案会被遍历 \(O(len)\) 次),但是,我们可以对当前分治的区间 \([l,r]\) 记 \(mi\) 表示会覆盖 \([l,r]\) 的所有解锁方案中费用最小的。
这样我们就不用把所有解锁方案都递归给左右儿子中。
分析一下现在的复杂度,发现每一个解锁方案只会被遍历 \(O(\log n)\) 次了(可以类比线段树),然后这样复杂度就是正确的了。
个人感觉复杂度是 \(O(n\log n)\) 的,但是网上说是 \(O(n\log^2 n)\) 的,不清楚哎...
一个小细节:上面的dp我们无法处理开始的状态,所以可以在前缀求最大值的时候加上 从这个点进入第 \(2\) 行 这样的一种转移方法。
- 另一种做法
除了用cdq去优化 \(n^2\) 的转移之外,还有一种比较高级的做法。
就是你去考虑上面的转移,很多都是无用的转移。
粗略的说,就是你 \(f_i\) 还是这么设,但是我可以证明,除了要用到的第一个解锁方案\((l_{p_1},r_{p_1},z_{p_1})\),其余剩下的所有解锁方案 \((l_{p_i},r_{p_i},z_{p_i})\) 都可以只从 \(l_{p_i}-1\) 这个位置转移给 \(l_{p_i}\sim r_{p_i}\) 这些位置。
为什么?可以感性证明,因为你选的最优方案就是 \(p_i\) 这些解锁方案,那么也就意味着这一大段上的贡献都会被计算(包括中间路径上的 和 解锁方案的花费。)而每一次的转移只是看什么时候会计算到这些贡献,所以可以就按最左段的转移来作为这整个解锁方案带来的转移是可以的。
详细的证明在这。
但是还是有第一个解锁方案不能处理,但其实可以反过来,这样就只有最后一次转移是任意转移,然后直接用线段树维护就行了。
启发
- 对于走路问题可以考虑dp。
- cdq分治优化 \(n^2\) dp的一个好例子。
- 考虑转移中哪些是无效的(一般都可以转化成 有效转移点就是最左边的点)