好难。Hootime 是不是不适合学 OI。
以下的
opt(i) 表示位置
i 的最优决策点,
optj(i) 表示只考虑起始点到
j 的情况下
i 的最优决策点。
从四边形不等式讲起
四边形不等式
若无特殊说明,以下均保证
a≤b≤c≤d。
是一个不等式(废话),长这样:
w(a,c)+w(b,d)≤w(a,d)+w(b,c)(1)
其中
a≤b≤c≤d。
这个东西还有另一种形式:
w(a,b)+w(a+1,b+1)≤w(a+1,b)+w(a,b+1)(2)
简记为「交叉小于包含」。
下证两个式子等价。
充分性很容易证明,只需要将代入
a′=a,b′=a+1,c′=b,d′=b+1 代入
(1) 即可。
必要性:
w(a+1,c+1)−w(a+1,c)≤w(a,c+1)−w(a,c)(1.1)
那么我们使用数学归纳法,假设
w(b−1,c+1)−w(b−1,c)≤w(a,c+1)−w(a,c)
由
(1.1) 得,
w(b,c+1)−w(b,c)≤w(b−1,c+1)−w(b−1,c)
合并两式,得
w(b,c+1)−w(b,c)≤w(a,c+1)−w(a,c)(1.2)
w(b,c+1)−w(a,c+1)≤w(b,c)−w(a,c)(1.3)
我们再次使用数学归纳法,假设
w(b,d−1)−w(a,d−1)≤w(b,c)−w(a,c)
那么同理,可得:
w(b,d)−w(a,d)≤w(b,c)−w(a,c)(1.4)
移项得:
w(a,c)+w(b,d)≤w(a,d)+w(b,c)
不严谨地说如果你的大脑比较擅长空间想象的话,由
w(a+1,b+1)−w(a+1,b)≤w(a,b+1)−w(a,b) 和
w(a+1,b+1)−w(a,b+1)≤w(a+1,b)−w(a,b) 可得满足四边形不等式的函数在两个维度上的二阶混合差分都是非正的。换句话说,固定其中任意一维,另一位的差分都是非正的。
经典结论
1
对于问题:
fi=min1≤j≤iw(j,i),如果
w 满足四边形不等式且
i<i′,有
opt(i)≤opt(i′)。
下证性质的正确性。
反证法。设存在
i<i′ 且
opt(i)>opt(i′)。
设
j←opt(i),j′←opt(i′)。整理如上不等关系得
j′<j≤i<i′。
又由最优性可得,
w(j,i)<w(j′,i),w(j′,i′)<w(j,i′)。
将两式相加得
w(j,i)+w(j′,i′)<w(j′,i)+w(j,i′),即
w(j′,i)+w(j,i′)>w(j,i)+w(j′,i′)。
我们用
a,b,c,d 代替
j′,j,i,i′,发现式子变成了
w(a,c)+w(b,d)>w(a,d)+w(b,c),与四边形不等式矛盾。
2
对于问题:
f(i)=min1≤j≤if(j−1)+w(j,i),如果
w 满足四边形不等式且
i<i′,有
opt(i)≤opt(i′)。
下证性质的正确性。感谢
Jink给我讲懂了这个证明。
引理:如果
w 满足四边形不等式且
i<i+1,有
opt(i)≤opt(i+1)。
设
j←opt(i),k∈[1,j−1]。
fj−1+w(j,i)≤fk−1+w(k,i)(2.1)
移项得:
fj−1−fk−1≤w(k,i)−w(j,i)(2.2)
又由于
w 满足四边形不等式,有
w(k,i)+w(j,i+1)≤w(j,i)+w(k,i+1)。移项得:
w(k,i)−w(j,i)≤w(k,i+1)−w(j,i+1)(2.3)
合并
(2.2),
(2.3) 得:
fj−1−fk−1≤w(k,i+1)−w(j,i+1)(2.4)
移项得:
fj−1+w(j,i+1)≤fk−1+w(k,i+1)(2.5)
从而证明了此时选
k 一定不优于选
j,因此
opt(i+1)∈/[1,j−1]。
对这个结论使用数学归纳法可以得到决策单调性,这里就不赘述了。
算法们
拿到决策单调性其实题就做了一大半了。剩下的就是选择算法并且实现。
选择算法主要需要看
w 的性质。
w(i,j) 能直接
O(1) 计算那当然是极好的,但是很多情况下
w 的性质没有这么好。有以下两种比较常见的性质:
- 移动访问:有点像莫队。具体就是 w(i,j) 只能通过 w(i±1,j) 或者 w(i,j±1) 来 O(1) 求得。
- 动态计算:w(i,j) 的计算依赖 f1∼i,你只能顺次计算 f 和 i。
这两个性质并不互斥,有可能同时出现。
整体二分
假设我们现在要处理
[L,R] 的
opt(i),已知
opt(i)∈[l,r]。
我们算出
[L,R] 的区间中点
mid,并且
暴力求得
opt(mid)。由于决策单调,我们知道了
∀i∈[L,mid−1],opt(i)∈[l,opt(mid)],∀i∈[mid+1,R],opt(i)∈[opt(mid),r]。
于是我们对两边分治一下即可。考虑分治树,树高为
logn,每层我们总共花费了
O(n) 暴力求出了
opt(mid),于是时间复杂度为
O(nlogn)。
整体二分是可以处理移动访问的情况的(先把
r 移动到
mid,再顺次移动
l),但是由于需要先计算
opt(mid),所以不能做动态计算。
如果要处理动态计算的话当然可以使用 cdq 分治,但是
O(nlog2n) 明显不是很优,可以使用后面提到的简化 LARSCH 算法。
二分队列
我们换一个思路,考虑当前及以前的决策对之后的决策的影响。
我们加强【经典结论 1】的结论。
对于问题:
f(i)=min1≤j≤iw(j,i),只考虑
1≤j≤k 的决策,如果
w 满足四边形不等式且
i<i′,有
optk(i)≤optk(i′)。
换句话说就是如果只考虑
1≤j≤k 的决策,那么仍然有决策单调性。
下证加强结论的正确性。
我们有
w′(j,i)=w(j,i)+M[j>k],其中
M 为充分大的正实数。我们发现
w′ 仍然满足四边形不等式。
这时我们考虑问题
f′(i)=min1≤j≤iw′(j,i)。由【经典结论 1】可得该问题的决策单调性。明显在该问题中大于
k 的决策是不优的,于是我们得到
opt′(i)=optk′(i)=optk(i),结论得证。
那么我们就可以对已有的决策
k∈[1,j],对于将来的决策
k′∈(j,n],我们可以用一个队列存下来
[1,k] 的每一个决策及其在
(k,n] 为最优决策的区间,这些区间并起来应该为
(k,n] 并且不交。
这么讲可能有点抽象,下面是算法流程。可以适当类比斜率优化中的单调队列。
- 我们正在处理决策 j。如果我们发现队列的队头的最优区间的右端点是 j−1,那么把它弹掉。其实不弹问题也不大来着,但是会多一次二分加大常数。
- 然后我们要把 j 入队。如果你想问为什么不是先处理 fj 再入队那么问题有 w(j,j) 的转移。
- 我们设队尾元素为 j′,其最优区间的左端点为 lj′,右端点为 rj′。
- 如果决策 j 在 lj′ 优于 j′,那么 j′ 可以完全被 j 代替,因而没有存在的必要了,我们弹掉 j′。重复这个过程直到没有能被弹掉的 j′。
- 如果队列空了,我们向队列中加入 (j,j,n),表示认为 j 是之后所有决策的最优决策点。
- 否则,我们在当前的队尾的最优区间 [lj′,rj′] 中二分出一个位置 p,满足 p 是第一个 j 优于 j′ 的位置。然后,我们将队尾修改为 (j′,lj′,p−1),再向队列中加入 (j,p,n)。特别地,如果二分出 p>n,那么 j 在整个区间上都不优,不用入队。
- 然后我们发现队头就是 j 的最优决策,更新 j 即可。
每个决策都只会被入队和出队一次,出队是
O(1) 的,入队是
O(logn) 的,因而时间复杂度为
O(nlogn)。
这个算法很明显可以处理动态计算的问题,但是处理不了移动访问。
简易 LARSCH 算法
这个算法其实可以部分类比 CDQ 分治,有点像又不是很像的样子。
我们设计一个函数
solve(l,r),作用是算出
fl∼r。这里按照 Hootime 的习惯使用了闭区间。
solve(l,r) 要运行有两个前提:
- 0∼l−1 的 f 和决策点都处理了。
- 对于 r,只考虑 0∼l−1 的情况下,f 和决策点都处理了。
solve(l,r) 做了这些事:
- 如果 l=r,那么 把 opt(l−1)∼optl−1(r) 转移到 r,然后退出。
- 将 opt(l−1)∼optl−1(r) 转移到 mid。
- solve(l,mid)。
- 将 opt(l)∼opt(mid) 转移到 r。
- solve(mid+1,r)。
为什么这个东西是对的?
首先我们考虑
solve(k,k)。由前提,
0∼k−1 的
f 和决策点都处理了,所以对于
k 的处理是对的。
然后我们考虑
solve(l,r)。假设
solve(l,mid),solve(mid+1,r) 的计算没有问题。第二步满足了第三步的递归前提,第四步满足了第五步的递归前提,所以递归的前提都是满足的,所以计算是正确的。
最后我们考虑
solve(1,n)。它的递归前提是容易保证的。综上,算法是正确的。
关于复杂度,我们每
层递归都相当于把整个序列遍历了一遍,所以复杂度是
O(nlogn)。
更快的算法?
学术上,有 SMAWK 算法、Wliber 算法等很多近线性或线性的算法,但是由于编码难度等原因在 OI 中尚未广泛应用。
扩展阅读