是一种可以针对i j具有某种单调性来用斜率优化转移的一种方式.
例:BZOJ1010 玩具装箱toy
很容易的写出方程,令表示解决前个的最小花费.
那么转移也很明显就是:.
很显然复杂度是的,如何优化呢.
斜率优化第一步假如是一次函数或许可以直接分析得到单调性.但是这个是二次函数,二次函数可以分析前面两个的关系分析出单调性,比如说设,令在决策点的转移更优,得到一组不等式.
首先把设置成.然后把设置成.
那么不等式就变成了:.
进一步化简可以得到:
到这时已经是一个斜率公式了.由的定义来说是递增的,对于来说它们两点连线的斜率假如大于,那么一定是会被淘汰的.答案最优解显然就是第一个大于斜率的.显然斜率是递增的,因为是递增的.当加入一个点,当它与队尾的斜率小于队尾与前面的斜率时,队尾就可以pop了.因为前面的推论.考虑当前k与前一个构成的一定是小于的.不优且影响单调性.
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e4+5;
const int mod=998244353;
const double eps=1e-9;
int c[N];
ll sum[N],f[N];
int n,l;
int q[N],L=0,R=0;
double A(int u)
{
return sum[u]+u-l-1;
}
double B(int u)
{
return sum[u]+u;
}
double solve(int a,int b)
{
return (double)(f[a]+B(a)*B(a)-f[b]-B(b)*B(b))/(double)(B(a)-B(b));
}
void solve()
{
scanf("%d%d",&n,&l);
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);
sum[i]=sum[i-1]+c[i];
}sum[0]=f[0]=0;
for(int i=1;i<=n;i++)
{
while(L<R&&solve(q[L+1],q[L])<=2*A(i)) L++;
int j=q[L];
f[i]=f[j]+(sum[i]-sum[j]-l+i-j-1)*(sum[i]-sum[j]-l+i-j-1);
while(L<R&&solve(q[R],q[R-1])>solve(i,q[R])) R--;
q[++R]=i;
}
printf("%lld\n",f[n]);
}
int main()
{
int T=1;
// scanf("%d",&T);
while(T--) solve();
return 0;
}
有个细节必须队列开始必须,不是只有队列里面有两个元素才能判断斜率转移,事实队列里一个元素和前一个元素也要判断转移.