UOJ#104. 【APIO2014】Split the sequence 动态规划 斜率优化

原文链接www.cnblogs.com/zhouzhendong/p/UOJ104.html

题解

首先证明一个结论:对于一种分割方案,分割的顺序不影响最终结果。

证明:对于树 a[x] 和 a[y] ,如果 x 与 y 之间有分割,那么它们对答案的贡献就是 a[x] * a[y] ,否则无贡献。

于是问题转化成 DP: 设 dp[i][j] 表示把前 j 个数分成 i 段的最大收益,那么:

设 

$$s[k] = \sum_{i=1}^{k} a[i]$$

$$dp[i][j] = \max_{0\leq k<j}(dp[i-1][k] + (s[j] - s[k])s[k])$$

直接斜率优化即可。

代码

#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define fi first
#define se second
#define real __zzd001
#define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
#define outarr(a,L,R) printf(#a"[%d...%d] = ",L,R);\
						For(_v2,L,R)printf("%d ",a[_v2]);puts("");
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef vector <int> vi;
LL read(){
	LL x=0,f=0;
	char ch=getchar();
	while (!isdigit(ch))
		f|=ch=='-',ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
const int N=100005,K=205;
const LL INF=1e18;
int n,k;
int a[N],s[N];
LL dp[K][N];
int pre[K][N];
LL x[N],y[N];
int q[N],head,tail;
double pos(int a,int b){
	return (double)(x[a]-x[b])/(y[b]-y[a]);
}
void Push(int i,int j){
	if (dp[i][j]>=0){
		x[j]=dp[i][j]-(LL)s[j]*s[j];
		y[j]=s[j];
		while (head<=tail&&y[j]==y[q[tail]]){
			if (x[j]<=x[q[tail]])
				return;
			tail--;
		}
		while (head<tail&&pos(q[tail],j)<=pos(q[tail-1],q[tail]))
			tail--;
		q[++tail]=j;
	}
}
int main(){
	n=read(),k=read()+1;
	For(i,1,n)
		a[i]=read(),s[i]=s[i-1]+a[i];
	For(i,0,K-1)
		For(j,0,N-1)
			dp[i][j]=-INF;
	dp[0][0]=0;
	For(i,1,k){
		head=1,tail=0;
		clr(x),clr(y);
		Push(i-1,0);
		For(j,1,n){
			while (head<tail&&pos(q[head],q[head+1])<=s[j])
				head++;
			if (head<=tail){
				dp[i][j]=x[q[head]]+y[q[head]]*s[j];
				pre[i][j]=q[head];
			}
			Push(i-1,j);
		}
	}
	cout<<dp[k][n]<<endl;
	int x=pre[k][n];
	while (--k)
		printf("%d ",x),x=pre[k][x];
	return 0;
}
/*
dp[i][j] = max_{0<=k<j}(dp[i-1][k] + (s[j] - s[k]) * s[k])
dp[i][j] = max_{0<=k<j}(dp[i-1][k] - s[k] * s[k] + s[j] * s[k])
*/

  

posted @ 2019-04-10 22:24  zzd233  阅读(199)  评论(0编辑  收藏  举报