Luogu P3195 [HNOI2008]玩具装箱

题目
预处理\(C\)的前缀和\(sum\)。设前\(i\)个物品的最小答案为\(f\)
\(f_i=\max\limits_{j\in[1,i)}(f_j+(sum_i-sum_j-L)^2)\)
拆开就是\(f_i=\max\limits_{j\in[1,i)}(f_j+sum_i^2+sum_j^2+L^2-2Lsum_i-2Lsum_j-2sum_isum_j)\)
稍微整理一下\(f_i=\max\limits_{j\in[1,i)}(f_j+sum_j^2-2Lsum_j-2sum_isum_j)+sum_i^2+L^2-2Lsum_i\)
然后直接斜率优化。
代码是以前写的,建议斜率交叉相乘后判断大小避免精度误差。

#include<bits/stdc++.h>
#define N 50001
using namespace std;
inline int read()
{
    int x=0;
    char ch=getchar();
    while(ch<'0'||ch>'9')
        ch=getchar();
    while(ch>='0'&&ch<='9')
        x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x;
}
inline int max(int a,int b)
{
	return a>b? a:b;
}
long long sum[N],f[N],q[N],L;
inline double slope(int i,int j)
{
	return (double)(f[i]-f[j]+(sum[i]-sum[j])*(sum[i]+sum[j]+(L<<1)))/(double)(sum[i]-sum[j]);
}
int main()
{
	register int n=read();
	L=read()+1;
	for(register int i=1;i<=n;++i)
		sum[i]=sum[i-1]+read()+1;
	register int hd=1,tl=1;
	for(register int i=1;i<=n;++i)
	{
		while(hd<tl&&slope(q[hd],q[hd+1])<2*sum[i])
			++hd;
		f[i]=f[q[hd]]+(sum[i]-sum[q[hd]]-L)*(sum[i]-sum[q[hd]]-L);
        while(hd<tl&&slope(i,q[tl-1])<slope(q[tl-1],q[tl]))
			--tl;
        q[++tl]=i;
    }
    return printf("%lld",f[n]),0;
}
posted @ 2019-10-27 22:33  Shiina_Mashiro  阅读(111)  评论(0)    收藏  举报