玩具装箱toy「HNOI 2008」

题意

给定一个序列,可以将其分为任意段,每一段的值为\((len+L+\sum C_i)\),其中\(L,C\)给定。

求最大值。


思路

状态转移方程为\(f[i]=min(f[j]+(i-j+sum[i]-sum[j]-L-1)^2)\)

涉及到了平方直接展开比较恶臭,考虑将常数与变量分离,可以得到\(f[i]=f[j]+(sum[i]+i-L-1-sum[j]-j)^2\)

现在再展开就解决了。

\(end\)

代码

#include <bits/stdc++.h>

using namespace std;

namespace StandardIO {

	template<typename T>inline void read (T &x) {
		x=0;T f=1;char c=getchar();
		for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
		for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
		x*=f;
	}

	template<typename T>inline void write (T x) {
		if (x<0) putchar('-'),x*=-1;
		if (x>=10) write(x/10);
		putchar(x%10+'0');
	}

}

using namespace StandardIO;

namespace Project {
	#define int long long
	
	const int N=50005;
	
	int n,l,head,tail;
	int sum[N],dp[N],queue[N];
	
	inline double slope (int x,int y) {
		return (double)(dp[x]+(sum[x]+x+l+1)*(sum[x]+x+l+1)-dp[y]-(sum[y]+y+l+1)*(sum[y]+y+l+1))/(double)(sum[x]+x-sum[y]-y);
	}

	inline void MAIN () {
		read(n),read(l);
		for (register int i=1; i<=n; ++i) {
			read(sum[i]),sum[i]+=sum[i-1];
		}
		head=tail=1,queue[head]=0;
		for (register int i=1; i<=n; ++i) {
			while (head<tail&&slope(queue[head],queue[head+1])<=2*(sum[i]+i)) ++head;
			dp[i]=dp[queue[head]]+(sum[i]+i-sum[queue[head]]-queue[head]-l-1)*(sum[i]+i-sum[queue[head]]-queue[head]-l-1);
			while (head<tail&&slope(queue[tail-1],queue[tail])>=slope(queue[tail],i)) --tail;
			queue[++tail]=i;
		}
		write(dp[n]);
	}
	
	#undef int
}

int main () {
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	Project::MAIN();
}

posted @ 2019-08-16 13:13  Ilverene  阅读(142)  评论(0编辑  收藏  举报