BZOJ1010 – [HNOI2008]玩具装箱toy(斜率DP)

BZOJ1010

【分析】首先直接求dp方程为:dp[i] = MIN{ dp[j]+(sum[i]-sum[j]-(1+L))^2 }(j < i);

直接求n^2必然超时,这是第一个斜率DP:

1.证明决策单调性

设在状态i处的k决策优与j决策,即

dp[k]+(f[i]-f[k]-c)^2<=dp[j]+(f[i]-dp[j]-c)^2

对于后的某状态t,dp[t]=dp[i]+v;

要证明dp[k]+(f[t]-f[k]-c)^2<=dp[j]+(f[t]-f[j]-c)^2

只要证

dp[k]+(f[i]+v-f[k]-c)^2<=dp[j]+(f[i]+v-f[j]-c)^2

只要证

dp[k]+(f[i]-f[k]-c)^2+2*v*(f[i]-f[k]-c)+v^2<=dp[j]+(f[i]-f[j]-c)^2+2*v*(f[i]-f[j]-c)+v^2

只要证

2*v*(f[i]-f[k]-c)<=2*v*(f[i]-f[j]-c)

即f[k]>=f[j](显然)

证明完毕

2.求斜率方程,一般化成左边为j,k,右边为i的方程

因为dp[k]+(f[i]-f[k]-c)^2<=dp[j]+(f[i]-f[j]-c)^2

展开

dp[k]+f[i]^2-2*f[i]*(f[k]+c)+(f[k]+c)^2<=dp[j]+f[i]^2-2*f[i]*(f[j]+c)+(f[j]+c)^2

dp[k]-2*f[i]*(f[k]+c)+(f[k]+c)^2<=dp[j]-2*f[i]*(f[j]+c)+(f[j]+c)^2

即(dp[k]+(f[k]+c)^2-dp[j]-(f[j]+c)^2)/2*(f[k]-f[j])<=f[i]

3.则(dp[k]+(f[k]+c)^2-dp[j]-(f[j]+c)^2)/2*(f[k]-f[j])就是斜率方程,然后使用单调队列维护凸包;

令f(k,j)为斜率方程

队首:当f(q[x+1],q[x]) <= f[i]时说明q[x+1]比q[x]更优,所以删掉q[x];

队尾:当f(i,q[y]) < f(q[y-1],q[y])时,说明i比q[y]更优,删掉q[y];

【AAC CODE】76ms

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f, MAXN = 50000+10, MAXIN = 10000;
char buf[MAXIN], *ps = buf, *pe = buf+1;
inline void rnext(){
	if(++ps == pe) pe = (ps = buf)+fread(buf,sizeof(char),sizeof(buf)/sizeof(char),stdin);
}
template <class T>
inline bool in(T &ans)
{
	ans = 0;
	T f = 1;
	if(ps == pe) return false;
	do{ rnext(); if('-' == *ps) f = -1;} while(!isdigit(*ps) && ps != pe);
	if(ps == pe) return false;
	do{ ans = (ans<<1)+(ans<<3)+*ps-48;rnext();}while(isdigit(*ps) && ps != pe);
	ans *= f;
	return true;
}

LL s[MAXN],c,dp[MAXN];
inline LL f(int k, int j)
{
	return (dp[k]-dp[j]+2*s[k]*c-2*s[j]*c+s[k]*s[k]-s[j]*s[j]);
}
inline LL down(int k, int j)//化除法为乘法,提高精度和时间效率
{
	return (s[k]-s[j])<<1;
}
int q[MAXN];
int main()
{
#ifdef SHY
	freopen("d:\\1.txt", "r", stdin);
#endif
	int n;
	while(in(n))
	{
		in(c);
		int a;
		c++;
		repe(i,1,n) in(a), s[i] = s[i-1]+a;
		repe(i,1,n) s[i] += i;
		int x = 1, y = 1;
		repe(i,1,n)
		{
			while(x < y && f(q[x+1],q[x]) <= s[i]*down(q[x+1],q[x])) x++;
			int j = q[x];
			dp[i] = dp[j]+(s[i]-s[j]-c)*(s[i]-s[j]-c);
			while(x < y && f(q[y],i)*down(q[y-1],q[y]) <= f(q[y-1],q[y])*down(q[y],i)) y--;
			q[++y] = i;
		}
		printf("%lld\n",dp[n]);
	}
	return 0;
}

 

发表评论

邮箱地址不会被公开。 必填项已用*标注

请输入正确的验证码