HYSBZ-1010
斜率优化推导
其中sum为前缀和
- 首先写出一般的状态转移方程:
dp[i] = min(dp[j]+(i-j-1+sum[u]-sum[j]-L)^2)
- 然后把平方打开,提取出和j有关的项,
dp[i] = min(dp[j]+(j+sum[j])^2-2*(j+sum[j])*(i-1+sum[i]-L)+(i-1+sum[i]-L)^2)
- 把无关的i的项去除
dp[i] = min(dp[j]+(j+sum[j])^2-2*(j+sum[j])*(i-1+sum[i]-L))
(一定注意括号问题,括号阔错了容易出错) - 还原,凑y = kx+b的形式 首先
- 有关i的无关项常数b
b = dp[i]
- 有关i的无关项斜率k
k = 2*(i-1-sum[i]-L)
- 有关j的x
x = j+sum[j]
- 有关j的y
y = (j+sum[j])^2+dp[j]
- 有关i的无关项常数b
- 得到式子
b = y -kx
即y = kx+b
通过上面的式子写出四个函数
ll getx(ll j){return j+sum[j];}
ll gety(ll j){return pow(j+sum[j],2)+dp[j];}
ll getk(ll i){return 2*(i-1+sum[i]-l);}
ll getb(ll i){return b = dp[i];}
//
// main.cpp
// 斜率优化_玩具_HYSBZ-1010
//
// Created by 陈冉飞 on 2019/8/12.
// Copyright © 2019 陈冉飞. All rights reserved.
//
#include <bits/stdc++.h>
//#include <iostream>
using namespace std;
typedef long long ll;
#define maxn 500050
ll dp[maxn],a[maxn],sum[maxn],q[maxn]; //dp[i]表示前i件的最小值
ll n,l,i,j,y,k,x,b,head,tail; //斜率优化还是需要有单调队列维护
#include <cmath>
ll getx(ll j){return j+sum[j];}
ll gety(ll j){return (j+sum[j])*(j+sum[j])+dp[j];}
ll getk(ll i){return 2*(i-1+sum[i]-l);}
ll getb(ll i){return dp[i];}
int main(int argc, const char * argv[]) {
scanf("%lld%lld",&n,&l);
for (i = 1; i <= n; i++) scanf("%lld",&a[i]);
//计算前缀和
sum[1] = a[1];
for (i = 2; i <= n; i++) sum[i] = sum[i-1] +a[i];
// for (i = 1; i <= n; i++) cout<<i<<" "<<sum[i]<<endl;
q[tail] = 0;
for (i = 1; i <=n; i ++) {
ll k=2*(i-1-l+sum[i]);
while(head<tail&&gety(q[head+1])-k*getx(q[head+1])<=gety(q[head])-k*getx(q[head])) ++head;
dp[i]=gety(q[head])-k*getx(q[head])+k*k/4;
while(head<tail&&(gety(i)-gety(q[tail-1]))*(getx(i)-getx(q[tail]))>=(gety(i)-gety(q[tail]))*(getx(i)-getx(q[tail-1]))) --tail;
q[++tail]=i;
}
cout<<dp[n]<<endl;
return 0;
}