bzoj.3675 [Apio2014]序列分割

这题依旧是斜率优化DP。
首先我们可以发现,它的分割顺序对答案没有影响:
假设我们要将一块分为三部分,每部分的和分别为a,b,c
a * (b+c)+b * c=a * b+a * c+b * c
(a+b) * c+a * b=a * c+b * c+a * b
所以,我们可以按顺序分了。
DP式:

f[i]=max{f[j]+a[j] * (a[i]-a[j])}

这里的a[i]表示原a[]的前缀和

斜率优化,我们可以得出:

f[j]-a[j]2-f[k]+a[k]2/(a[k]-a[j])<a[i]

上标:

#include<cstdio>
#define db double
#define ll long long
#define N 100010
using namespace std;
int n,K,g[N],l,len,x,las;
ll f[2][N],a[N];

inline int read()
{
	int x=0; char c=getchar();
	while (c<'0' || c>'9') c=getchar();
	while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

db solve(int x,int y) {return a[x]==a[y] ? -1e9:(db)(f[las][x]-a[x]*a[x]-f[las][y]+a[y]*a[y])/(a[y]-a[x]);}

int main()
{
//	freopen("3675.in","r",stdin);
//	freopen("3675.out","w",stdout);
	n=read(),K=read();a[0]=0;
	for (int i=1;i<=n;i++) a[i]=read()+a[i-1]; 
	x=0,las=0;
	for (int j=1;j<=K;j++)
	{
		l=len=0;x^=1;
		for (int i=1;i<=n;i++)
		{
			while (l<len && solve(g[l+1],g[l])<a[i]) l++;
			f[x][i]=f[las][g[l]]+a[g[l]]*(a[i]-a[g[l]]);
			while (len>l && solve(g[len],g[len-1])>solve(i,g[len])) len--;
			g[++len]=i;
		}
		las=x;
	}
	printf("%lld
",f[las][n]);
	return 0;
}
转载需注明出处。
原文地址:https://www.cnblogs.com/jz929/p/11817592.html