P3648 [APIO2014]序列分割

首先分的顺序不影响最后的总得分。比如说三块内部的总得分是 (a,b,c)(c(a+b)+ab=a(b+c)+bc=ab+ac+bc),显然每次分三块讨论可以适用于所有情况。

所以可以令 (f_{i,l}) 表示前 (i) 个元素分了 (l) 个块的最大总得分。对 (a) 做一遍前缀和。

转移方程也很好写:

[f_{i,l}=min{f_{j,l-1}+a_j imes(a_i-a_j)|l-1leq j<i} ]

考虑两个决策 (j,k(j<k)),若 (j)(k) 优:

[f_{j,l-1}+a_j imes(a_i-a_j)>f_{k,l-1}+a_k imes(a_i-a_k) ]

[f_{j,l-1}-f_{k,l-1}>a_k imes a_i-a_k^2-a_j imes a_i+a_j^2 ]

[f_{j,l-1}-a_j^2-f_{k,l-1}+a_k^2>(a_k-a_j) imes a_i ]

[dfrac{f_{j,l-1}-a_j^2-f_{k,l-1}+a_k^2}{a_k-a_j}>a_i ]

因为 (a_i) 随着 (i) 的增大单调不减,所以对 ((-a_i,f_{i,l-1}-a_i^2)) 维护一个下凸包。

时间复杂度 (Oleft(nk ight))

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100005
#define Db double
#define Min(x,y)((x)<(y)?x:y)
#define For(i,x,y)for(i=x;i<=(y);i++)
#define Down(i,x,y)for(i=x;i>=(y);i--)
ll g[N],f[N];
int pre[N][205],a[N],pos[N],que[N];
Db slope(int j,int k)
{
	return Db(g[j]-1LL*a[j]*a[j]-g[k]+1LL*a[k]*a[k])/Db(a[k]-a[j]);
}
int main()
{
	int n=0,m,i,head,tail,l,k;
	scanf("%d%d",&m,&k);
	For(i,1,m)
	{
		scanf("%d",&a[n+1]);
		if(a[n+1])
		{
			n++;
			a[n]+=a[n-1];
			pos[n]=i;
		}
	}
	k=Min(k,n);
	For(l,1,k)
	{
		head=tail=1;
		For(i,1,n)
		{
			while(head<tail&&slope(que[head],que[head+1])<=a[i])head++;
			pre[i][l]=que[head];
			f[i]=g[que[head]]+1LL*a[que[head]]*(a[i]-a[que[head]]);
			while(head<tail&&slope(que[tail-1],i)>=slope(que[tail],i))tail--;
			que[++tail]=i;
		}
		For(i,1,n)g[i]=f[i];
	}
	printf("%lld
",f[n]);
	i=n;
	Down(l,k,1)printf("%d ",pos[pre[i][l]]),i=pre[i][l];
	return 0;
}
原文地址:https://www.cnblogs.com/May-2nd/p/14248482.html