[HNOI2008]玩具裝箱TOY(斜率優化)
阿新 • • 發佈:2019-01-06
題目連結
題意:有編號為
的N件玩具,第 i 件玩具經過壓縮後變成一維長度為
。要求在一個容器中的玩具編號是連續的,同時如果將第 i 件玩具到第 j 個玩具放到一個容器中,那麼容器的長度將為
。如果容器長度為 x ,其製作費用為
.其中 L 是一個常量。容器數目長度不限。求最小費用。
這道題是斜率優化的經典題了qvq
當然dp順序肯定是從前到後了
分析一下答案式
用f(j)來更新f(i)
設
這裡面 隨j改變的量是
和
所以移項得
將b[j]看作x,
看作y,這個式子就可以看作一條斜率為
的直線
f[i]即當上述直線過點
時,直線在y軸的截距加
而題目即為找這個截距的最小值
由於sum[i]隨i遞增 所以a[i],b[i]都遞增
所以點
是從左到右排列的
用單調棧維護一下凸包
像做線性規劃一樣做一個切線就行了
也就是二分斜率(Pj,Pj+1) < 2a[i]
update:貌似不用二分
因為a[i]遞增要查詢的斜率也遞增
那單調佇列維護就行了qvq
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 1e5 + 5;
const int K = 2e5;
int n, L;
double sum[N], f[N];
int que[N], head, tail;
inline double a(int x){return sum[x]+x;}
inline double b(int x){return sum[x]+x+1+L;}
inline double X(int x){return b(x);}
inline double Y(int x){return f[x]+b(x)*b(x);}//注意這裡不可以用define qvq
//a[i] = sum[i] + i, b[i] = sum[i] + i + 1 + L
//P(b[j],f[j]+b[j]^2)
inline double slope(int x, int y){
return (Y(y) - Y(x)) / (X(y) - X(x));
}
int main() {
scanf("%d%d", &n, &L);
for(int i = 1; i <= n; ++i){
scanf("%lf", &sum[i]);
sum[i] += sum[i - 1];
}
head = tail = 1;
for(int i = 1; i <= n; ++i){
while(head < tail && slope(que[head], que[head + 1]) < 2 * a(i)) ++head;
f[i] = f[que[head]] + (a(i) - b(que[head])) * (a(i) - b(que[head]));
//printf("b %lld\n", (long long)(a(i) - b(que[head])));
while(head < tail && slope(que[tail - 1], que[tail]) > slope(que[tail - 1], i)) --tail;
que[++tail] = i;
}
printf("%lld", (long long)f[n]);
return 0;
}