洛谷P2300 合并神犇
一道单调队列的模板题。
看到数据范围不难想到一维\(DP\)数组\(f_i\)表示将\(1\)到\(i\)的数合并后满足单调不降的最小合并次数。
考虑\(f_i\)是由什么转移得到的。发现\(f_i\)并不只是和\(f_{i-1}\)有关,实际上,由于合并的性质,\(f_i\)可以由前面任意一个\(f_j\)得到,但前提是由\(i\)到\(j\)合并得到的数要不小于为了得到\(f_j\)所合并后的序列的最后一个数。
我们设\(s_i\)表示\(p_i\)的前缀和,\(last_i\)表示得到\(f_j\)所合并后的序列的最后一个数。那么转移方程就是\(f_i=min\{f_j+i-j-1\}(s_i-s_j\geq last_j)\)。这样暴力转移是\(O(n^2)\)的。
考虑优化转移。对于\(k=j+1\)来说,\(f_k\leq f_j+1\)。因为如果\(f_k>f_j+1\)的话,我们只需要在\(f_j\)的基础上将新加入的数与前面合并即可得到\(f_k=f_j+1\)。而\(j\)每增加一,\(f_i+i-j-1\)一定会减少\(1\)。也就是说我们要找的\(j\)其实是满足\(s_i-s_j\geq last_j\)的最大的\(j\)。
对式子\(s_i-s_j\geq last_j\)进行变形得到\(s_j+last_j\leq s_i\)。发现\(s_i\)是单调上升的,也就是对于一个固定的\(j\)来说,\(s_j+last_j\)的值越小,就越有可能在更多的转移中被选择。
对于\(j<k\),如果\(s_k+last_k\leq s_j+last_j\),也就是\(k\)在比\(j\)后入队的同时(即比\(j\)优),还比\(j\)更容易在更多的选择中被转移,那么\(j\)也就没用了,因为无论如何能选择\(j\)的情况一定可以选择\(k\)。
int l = 1, r = 0;
for(int i = 1; i <= n; i ++)
{
while(l <= r && s[q[l]] + last[q[l]] <= s[i]) l ++;
f[i] = f[q[l - 1]] + i - q[l - 1] - 1;
last[i] = s[i] - s[q[l - 1]];
while(l <= r && s[i] + last[i] <= s[q[r]] + last[q[r]]) r --;
q[++r] = i;
}

浙公网安备 33010602011771号