K-Anonymous Sequence
看了两年前自己的博客,真的好青涩,连 markdown
都不太会用。
确实观赏感不是很好。
学习真的是慢慢积累的过程,以前感觉理解很困难的事,随着知识的增长,现在都基本悟了。
problem
POJ3709
solution
设 f(i):f(i):f(i): 考虑到前 iii 个数,并在 iii 个数划分的最小花费。
可以直接列出状态转移,枚举前一段的位置 jjj ,则 [j+1,i][j+1,i][j+1,i] 要求相同,且长度要够长。
f(i)=min{f(j)+sum(i)−sum(j)+a[j+1]∗(i−j)}(j≤i−m)f(i)=\min\Big\{f(j)+sum(i)-sum(j)+a[j+1]*(i-j)\Big\}(j\le i-m)f(i)=min{f(j)+sum(i)−sum(j)+a[j+1]∗(i−j)}(j≤i−m)
其中 sum(i):sum(i):sum(i): 前 iii 个数的和,即前缀和。
注意到题目给的性质 a[i]a[i]a[i] 是单增的。
考虑两个合法转移点 j<kj<kj<k ,且 jjj 不优于 kkk。
即满足:f(j)+sum(i)−sum(j)+a[j+1]∗(i−j)≥f(k)+sum(i)−sum(k)+a[k+1]∗(i−k)f(j)+sum(i)-sum(j)+a[j+1]*(i-j)\ge f(k)+sum(i)-sum(k)+a[k+1]*(i-k)f(j)+sum(i)−sum(j)+a[j+1]∗(i−j)≥f(k)+sum(i)−sum(k)+a[k+1]∗(i−k)
化简得:f(j)−sum(j)−a[j+1]∗j−f(k)+sum(k)+a[k+1]∗k≥i∗(a[j+1]−a[k+1])f(j)-sum(j)-a[j+1]*j-f(k)+sum(k)+a[k+1]*k\ge i*\big(a[j+1]-a[k+1]\big)f(j)−sum(j)−a[j+1]∗j−f(k)+sum(k)+a[k+1]∗k≥i∗(a[j+1]−a[k+1])
一般来说,斜率优化是把 iii 的系数简化为 111 的分式形式。但是有些题目可能存在“平台”,即系数算出来为 000。
除法分母不能是 000,所以为了防止程序死掉,乘法形式才是最好选择。
但是因为不等式的存在必须要求 aaa 的单调性,否则中途的决策点选择有不等式的变号,就不是斜率优化了。
李超树才是我们的红太阳!
单调队列存储合法转移点,每次取最优的队首转移。
对于队首的维护是非常简单的。
但斜率优化难以理解的问题就在队尾的维护,即常说的凸包维护。
网上很多就直接上凸包图像,然后从直线斜率角度入手,这里不赘述。
以此题为例,用语言化的最优点选择来尝试阐释斜率问题。
一般会有,令 Y(j,k)=f(j)−sum(j)−a[j+1]∗j−f(k)+sum(k)+a[k+1]∗kX(j,k)=a[j+1]−a[k+1]Y(j,k)=f(j)-sum(j)-a[j+1]*j-f(k)+sum(k)+a[k+1]*k\quad X(j,k)=a[j+1]-a[k+1]Y(j,k)=f(j)−sum(j)−a[j+1]∗j−f(k)+sum(k)+a[k+1]∗kX(j,k)=a[j+1]−a[k+1]
如果只考虑两个选择点 (x,y)(x,y)(x,y),显然当 iii 增大后,不等式可能存在反向的问题,即 xxx 不一定就永远差于 yyy。【现在是要考虑 iii 后面点的转移,iii 点也是转移点】
所以是考虑三个点 (x,y,z),x<y<z(x,y,z),x<y<z(x,y,z),x<y<z【这对应着计算 iii 后,iii 与凸包上的最后一个点形成的直线,以及凸包最后两个点形成的直线,两个直线的维护】
因为本题是最小值,所以当 Y(x,y)X(x,y)≥Y(y,z)X(y,z)⇒Y(x,y)⋅X(y,z)≥Y(y,z)⋅X(x,y)\frac{Y(x,y)}{X(x,y)}\ge \frac{Y(y,z)}{X(y,z)}\Rightarrow Y(x,y)·X(y,z)\ge Y(y,z)·X(x,y)X(x,y)Y(x,y)≥X(y,z)Y(y,z)⇒Y(x,y)⋅X(y,z)≥Y(y,z)⋅X(x,y) 时,去掉 yyy。【维护凸包】
接下来解释为什么??
回到上面式子去,因为 aaa 是单增,所以 j<k⇒X(j,k)<0j<k\Rightarrow X(j,k)<0j<k⇒X(j,k)<0 。即 Y(j,k)X(j,k)≤i\frac{Y(j,k)}{X(j,k)}\le iX(j,k)Y(j,k)≤i,证明 jjj 不优于 kkk,且注意到 Y(j,k)X(j,k)\frac{Y(j,k)}{X(j,k)}X(j,k)Y(j,k) 是一个常数,与枚举点 iii 无关。
现在回到这里,对于某个 iii,如果 Y(y,z)X(y,z)≥i\frac{Y(y,z)}{X(y,z)}\ge iX(y,z)Y(y,z)≥i 那么一定有 Y(x,y)X(x,y)≥i\frac{Y(x,y)}{X(x,y)}\ge iX(x,y)Y(x,y)≥i。
即,如果 yyy 优于 zzz,那么 xxx 一定也优于 yyy,那就没有 yyy 什么事了。【对应的上凸包维护,即斜率逐渐递减】
code
#include <cstdio>
using namespace std;
#define int long long
#define maxn 500005
int T, n, m;
int a[maxn], q[maxn], f[maxn], sum[maxn];int Y( int j, int k ) {return f[j] - sum[j] + j * a[j + 1] - f[k] + sum[k] - k * a[k + 1];
}int X( int j, int k ) {return a[j + 1] - a[k + 1];
}signed main() {scanf( "%lld", &T );while( T -- ) {scanf( "%lld %lld", &n, &m );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] ), sum[i] = sum[i - 1] + a[i];int head = 1, tail = 0; q[++ tail] = 0;for( int i = 1;i <= n;i ++ ) {while( head < tail and Y( q[head], q[head + 1] ) >= i * X( q[head], q[head + 1] ) ) head ++;int j = q[head];f[i] = f[j] + sum[i] - sum[j] - a[j + 1] * ( i - j );int k = i - m + 1;if( k >= m ) {while( head < tail and Y( q[tail - 1], q[tail] ) * X( q[tail], k ) >= Y( q[tail], k ) * X( q[tail - 1], q[tail] ) )tail --;q[++ tail] = k;}}printf( "%lld\n", f[n] );}return 0;
}