[题解]luogu_P3195_玩具装箱toy(斜率优化

设$f[i]$表示装$i$个玩具用的最小花费,

 原转移方程:$f[i]=min(f[j]+(sum[i]-sum[j]+i-(j+1)+L)^2$

把与$i$和$j$相关的放到一起:$f[i]=min(f[j]+(sum[i]+i-(sum[j]+j+L+1))^2$

不断化简:设$a[i]=sum[i]+i,b[i]=sum[i]+i+L+1$,

则:$f[i]=min(f[j]+(a[i]-b[j])^2)$

对于$k,j$,若$j$比$k$优,则有:

$f[j]+(a[i])^2+(b[j])^2-2*(a[i])*(b[j])<=f[k]+(a[i])^2+(b[k])^2-2*(a[i])*(b[k])$

化简:$f[j]-f[k]+(b[j])^2-(b[k])^2<=2*(b[j]-b[k])*(a[i])$

写成斜率形式:$frac{f[j]-f[k]+(b[j])^2-(b[k])^2}{b[j]-b[k]}<=2*a[i]$

$a[i],b[i]$都是递增的,可以直接斜率优化

#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
const int maxn=50009;
int n;
ll l,f[maxn],s[maxn];
int q[maxn],head,tail;

inline ll checka(int j,int k){
    return f[j]-f[k]+(s[j]+j+l+1+s[k]+k+l+1)*(s[j]+j-s[k]-k);
}
inline ll checkb(int j,int k){
    return s[j]+j-s[k]-k;
}
int main(){
    scanf("%d%lld",&n,&l);
    for(int i=1;i<=n;i++)
    scanf("%lld",&s[i]);
    for(int i=1;i<=n;i++)s[i]+=s[i-1];
    head=tail=1;
    for(int i=1;i<=n;i++){
        while(head<tail && checka(q[head+1],q[head])<=2*(s[i]+i)*checkb(q[head+1],q[head]))head++;
        f[i]=f[q[head]]+(s[i]+i-s[q[head]]-q[head]-l-1)*(s[i]+i-s[q[head]]-q[head]-l-1);
        while(head<tail && checka(i,q[tail])*checkb(q[tail],q[tail-1])<=checka(q[tail],q[tail-1])*checkb(i,q[tail]))tail--;
        q[++tail]=i;
    }
    printf("%lld",f[n]);
}
原文地址:https://www.cnblogs.com/superminivan/p/11297707.html