下标序列与斜率优化

下标序列是我定义的一个东西。对于dp方程 f [ i ] = min ⁡ ( f [ j ] + c o s t ( i , j ) ) , l [ i ] ≤ j ≤ r [ i ] f[i] = \min(f[j] + cost(i,j)),l[i]\le j\le r[i] f[i]=min(f[j]+cost(i,j)),l[i]jr[i] 我们维护一个序列保存所有可以从那里转移来的位置。比如枚举到 i i i时,序列里存的就是 l [ i ] l[i] l[i] r [ i ] r[i] r[i]的所有数。那么我们最终的决策点 k k k一定是从这个序列里面选的。这个就叫下标序列。
因为要枚举 n n n次,每次下标序列中有 n n n个元素,所以复杂度是 O ( n 2 ) O(n^2) O(n2)
但是我们发现,对于相邻的 i i i,下标序列可能相差不大。于是我们考虑从这里优化。
考虑对全局维护一个下标序列F,每次枚举 i i i时在这个序列上面作修改,使得它成为每个i自己的下标序列。
如果每次我们可以快速地从当前下标序列中找到决策点,就可以大大提高效率。事实上,斜率优化就做了这件事情。斜率优化支持用 O ( 1 ) 或 O ( log ⁡ n ) O(1)或O(\log n) O(1)O(logn)的时间找到决策点和维护下标序列。具体地说,找决策点可以直接取队首元素或二分查找,维护序列可以用数组或平衡树。

接下来就比较跳跃地介绍斜率优化方法。考虑把下标序列中的每一个元素当做一个平面上的点,它的横纵坐标都可以由这个元素的值(就是这个下标)计算得到。我们用一个折线把这些点从左到右连起来。
然后对于每次询问,假设这个询问给了一个可以随意平移的直线,也就是给定一个斜率。我们要找的决策点,就是这个直线与折线在最下面的切点。
如果这个折线是一个向下凸的“弧”,那就可以二分答案,看斜率在哪里刚好比答案大,那就是答案。【1】
这个就很好啊,那怎么把它和dp方程联系起来呢?
刚才我们说了维护的是下标序列,并且有决策单调性。那么我们只要发现一个点以后不再可能是答案,那就可以把它踢出下标序列。刚才我们已经发现了!如果折线是向下凸的弧,那答案一定在这个弧上。如果折线不是向下凸的,那我们就找到向下凸的弧,然后不是弧上的就都踢掉就行了。
事实上,我们不是每次找一个弧然后删除一些点,而是维护这个弧,并且每次加入一个新点时,把它加入弧,然后把弧以上的点都删掉。其实这就是 O ( 1 ) O(1) O(1)维护凸包。如果新点总是在最右边加入,就用一个数组(栈)维护,每次先把后面的弹了再加入;否则就用平衡树 O ( log ⁡ n ) O(\log n) O(logn)维护。
回到刚才【1】的地方,我们知道二分答案比较慢,于是也可以用踢掉不需要的东西的办法。如果询问给的直线每次的斜率都比上一次大,那么所有斜率比它小的点就可以直接删除。这其实是一个单调队列,复杂度降为 O ( 1 ) O(1) O(1)

最后两个问题:怎么用下标序列计算点的坐标?每次询问给的直线是哪里来的?这个其实都是用dp方程变形得到的。我们把dp方程化成这样的方程 f [ i ] = g ( i ) h ( j ) + q ( i ) + s ( j ) f[i]=g(i)h(j)+q(i)+s(j) f[i]=g(i)h(j)+q(i)+s(j)
变成 s ( j ) = − g ( i ) h ( j ) − q ( i ) + f [ i ] s(j)=-g(i)h(j)-q(i)+f[i] s(j)=g(i)h(j)q(i)+f[i]
我们看到与 j j j有关的项,把 h ( j ) h(j) h(j)的看成横坐标, s ( j ) s(j) s(j)看成纵坐标,那么: y j = − g ( i ) x j − q ( i ) + f [ i ] y_j=-g(i)x_j-q(i)+f[i] yj=g(i)xjq(i)+f[i]
看, ( x j , y j ) (x_j,y_j) (xj,yj)就是下标序列每个对应的点,把它代入直线 y = − g ( i ) x − q ( i ) + f [ i ] y=-g(i)x-q(i)+f[i] y=g(i)xq(i)+f[i],其中i是枚举的可以当成常数,要求的是 f [ i ] f[i] f[i],使它最小。
懂了没?询问的斜率就是 − g ( i ) -g(i) g(i),每个点就是 ( h ( j ) , s ( j ) ) (h(j),s(j)) (h(j),s(j))。为什么要找切点?这样纵截距 f [ i ] f[i] f[i]就最小啊!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值