1. 程式人生 > >[HNOI2008]玩具裝箱TOY(斜率優化)

[HNOI2008]玩具裝箱TOY(斜率優化)

題目連結
題意:有編號為 1 N 1\cdots N 的N件玩具,第 i 件玩具經過壓縮後變成一維長度為 C i

C_i ​ 。要求在一個容器中的玩具編號是連續的,同時如果將第 i 件玩具到第 j 個玩具放到一個容器中,那麼容器的長度將為 x = j i +
k = i j C k
x=j-i+\sum\limits_{k=i}^{j}C_k
。​如果容器長度為 x ,其製作費用為 ( X L ) 2 (X-L)^2 .其中 L 是一個常量。容器數目長度不限。求最小費用。
1 N 50000 , 1 L , C i 1 0 7 1 \le N \le 50000,1 \le L,Ci \le 10^7

這道題是斜率優化的經典題了qvq
當然dp順序肯定是從前到後了
分析一下答案式
用f(j)來更新f(i)
X = i ( j + 1 ) + k = j + 1 i C k = s u m [ i ] + i s u m [ j ] j L 1 X = i-(j + 1)+\sum\limits_{k=j + 1}^{i}C_k = sum[i] + i - sum[j] - j - L - 1
a [ i ] = s u m [ i ] + i , b [ i ] = s u m [ i ] + i + 1 + L a[i] = sum[i] + i, b[i] = sum[i] + i + 1 + L
f [ i ] = f [ j ] + ( X L ) 2 = f ( j ) + ( a [ i ] b [ j ] ) 2 f[i] = f[j] + (X - L)^2 = f(j) + (a[i] - b[j]) ^ 2
這裡面 隨j改變的量是 b [ j ] , b [ j ] 2 b[j], b[j]^2 f [ j ] f[j]
所以移項得 2 a [ i ] b [ j ] + f [ i ] a [ i ] 2 = f [ j ] + b [ j ] 2 2⋅a[i]⋅b[j]+f[i]−a[i]^2=f[j]+b[j]^2
將b[j]看作x, f [ j ] + b [ j ] 2 f[j]+b[j]^2 看作y,這個式子就可以看作一條斜率為 2 a [ i ] 2a[i] 的直線
f[i]即當上述直線過點 P ( b [ j ] , f [ j ] + b [ j ] 2 ) P(b[j],f[j]+b[j]^2) 時,直線在y軸的截距加 a [ i ] 2 a[i]^2
而題目即為找這個截距的最小值
由於sum[i]隨i遞增 所以a[i],b[i]都遞增
所以點 1 i 1 1 \cdots i-1 是從左到右排列的
用單調棧維護一下凸包
像做線性規劃一樣做一個切線就行了
也就是二分斜率(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;
}