比如这个式子: (from [HNOI2008]玩具装箱TOY)
\[ f[i] = \min\{ f[j] + (sum[i] + i - sum[j] - j - L - 1) ^ 2 \} \]
大概有两种方法:
以上都是基本的转化, 现在考虑两种方法分别如何求最优值
首先都是要画图, 然后就会发现两种方法是极其类似的,
于是, 对于每个 \(i\), 可以在 \([1, i - 1]\) 这些点组成的凸包上的点取值, 用 \(i\) 的斜率一个一个比过去即可,
例如本题中保留下凸包, 对于两种方法, 都是找到第一个大于当前斜率的点
不难想到可以用二分来查找
然而, 许多题都满足 \(i\) 的斜率有单调性, 比如这题, 两种方法中斜率都是 \(2(sum[i] + i)\), 其中两个变量都是单增的, 所以单增
于是, 凸包中也是斜率不断增的, 对于每个 \(i\) , 把从凸包起点开始不断删(即上面的暴力过程), 直到大于当前斜率, 显然这样复杂度是线性的
接下来用本题具体演示一下
第一种:
设 \(A[j] = (sum[j] + j + L + 1)\)
\[ f[j] + A[j] ^ 2 = 2 \cdot (sum[i] + i) \cdot A[j] + f[i] - (sum[i] + i) ^ 2 \]
第二种:
设 \(A[j] = (sum[j] + j + L + 1)\)
\[ \frac{(f[j] + A[j] ^ 2) - (f[k] + A[k] ^ 2)}{a[j] - a[k]} \le 2 \cdot (sum[i] + i) \]
代码是一样的
int n;
const int MAXN = 5e4 + 10;
LL L, sum[MAXN], a[MAXN], f[MAXN];
struct Node
{
LL x, y;
} q[MAXN];
int h, t;
/*
0815
*/
int main()
{
n = in();
L = in();
for (int i = 1; i <= n; ++ i)
{
sum[i] = sum[i - 1] + in();
a[i] = (sum[i] + i + L + 1);
}
a[0] = L + 1;
f[0] = 0;
h = t = 1;
q[1].x = a[0], q[1].y = a[0] * a[0];
for (int i = 1; i <= n; ++ i)
{
while (h < t && (q[h + 1].y - q[h].y) <= 2 * (sum[i] + i) * (q[h + 1].x - q[h].x)) ++ h;
f[i] = q[h].y - 2 * (sum[i] + i) * q[h].x + (sum[i] + i) * (sum[i] + i);
Node now = (Node) { a[i], f[i] + a[i] * a[i] };
while (h < t && (now.y - q[t].y) * (q[t].x - q[t - 1].x) <= (q[t].y - q[t - 1].y) * (now.x - q[t].x)) -- t;
q[++ t] = now;
}
printf("%lld\n", f[n]);
return 0;
}
原文:https://www.cnblogs.com/Kuonji/p/11865015.html