P3195 [HNOI2008]玩具装箱 题解


首先先写dp方程

非常简单 \(\mathit{f}_{i}=\min(\mathit{f}_{j}+(\mathit{c}_{i}+i-j-1-L-\mathit{c}_{j})^2)\) (其中 \(\mathit{c}_{i}\) 表示长度前缀和)

然后发现括号中的东西十分离谱。

介于 \(\mathit{c}_{i} + i\) 为定值,将其赋为 \(x\)。而 \(j+1+L+\mathit{c}_{j}\)赋为 \(y\)

这样就能展开了

\(\mathit{f}_{i}=f_j+x^2-2xy+y^2\)

很明显这题要用决策单调性,但是由于有 \(2xy\) 这一项的存在,使得还得打一个该死的斜率优化。

移项写出一次函数形式 \(f_j+y^2=2xy+f_i-x^2\)

此时可以看成一个 \(y\)\(f_j+y^2\) 的一次函数。

由于没有负数,不用考虑斜率为负,虽然我还是写了。

然后就没了

#include<cstdio>
#define int long long
int read()
{
	int f=1,sum=0;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		sum=(ch-48)+(sum<<1)+(sum<<3);
		ch=getchar();
	}
	return sum*f;
}
void write(int x)
{
	if(x<0)putchar('-'),x=-x;
	if(x/10)write(x/10);
	putchar((x%10)+48);
}
const int N=50005;
int n,c[N],f[N],q[N],l,r,L;
signed main()
{
	n=read();
	L=read();
	for(int i=1;i<=n;i++)c[i]=read(),c[i]+=c[i-1];
	l=1,r=1;
	for(int i=1;i<=n;i++)
	{
		int nl=1,nr=r;
		while(nl<nr)
		{
			int mid=(nl+nr)>>1;
			int y1=c[q[mid+1]]+q[mid+1]+L+1,y2=c[q[mid]]+q[mid]+L+1;
			if(2*(c[i]+i)*(y1-y2)>=y1*y1-y2*y2+f[q[mid+1]]-f[q[mid]])nl=mid+1;
			else nr=mid;
		}
		f[i]=f[q[nl]]+(c[i]+i-q[nl]-1-c[q[nl]]-L)*(c[i]+i-q[nl]-1-c[q[nl]]-L);
		while(l<r&&((f[i]-f[q[r]])+((c[i]+i+1+L)*(c[i]+i+1+L)-(c[q[r]]+q[r]+1+L)*(c[q[r]]+q[r]+1+L)))*((c[q[r]]+q[r]+1+L)-(c[q[r-1]]+q[r-1]+1+L))<=((c[i]+i+1+L)-(c[q[r]]+q[r]+1+L))*((f[q[r]]-f[q[r-1]])+((c[q[r]]+q[r]+1+L)*(c[q[r]]+q[r]+1+L)-(c[q[r-1]]+q[r-1]+1+L)*(c[q[r-1]]+q[r-1]+1+L))))r--;
		q[++r]=i;
	}
	write(f[n]);
	return 0;
}
posted @ 2023-02-24 13:11  Gmt丶Fu9ture  阅读(26)  评论(0)    收藏  举报