bzoj3675[Apio2014] 序列分割

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3675

题目大意:

将一个长度为n的非负整数序列分割成k+1个非空的子序列,每一次分割会得到一个得分,为这次所分割成的两部分中元素和的乘积,求最大的得分

=================================================

题解:

斜率优化

首先我们要发现,对于最终分割位置是一样的方案,无论过程分割的顺序是如何的,它们的得分都是每一部分中元素和的乘积,即为相等的,也就是说分割顺序不影响得分。这个很容易证的吧,懒得打了..随便化化就好了..吧~(听说是易证的我

设f[k][i]表示第k次的分割点为i.

那么方程就能写成f[k][i]=f[k-1][j]+(sum[i]-sum[j])*sum[j];

我的话是将它反过来想,YY成i+1~n是已经搞好的,在1~i这里切一下j,获得的收益就是(sum[i]-sum[j])*sum[j]。

于是,把这个化一下用斜率优化做就好啦~

但是然而,化出来:-sum[i]*sum[j]+f[k][i]=f[k-1][j]-sum[j]^2;

斜率

分母是可能为0的,所以要么就移项把sum[j2]-sum[j1]乘过去,要么就像黄学长那样把可能为0的删了(不想改动的我翻到黄学长的这种做法也就这样做了233)。

啊最后最后,如果你真的来开f[k][n]的数组是会爆空间的,要弄成滚动的..

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
#define maxn 101000

int q[maxn],l,r,t;
LL f[2][maxn],a[maxn],sum[maxn];
double Y(int j){return f[1-t][j]-sum[j]*sum[j];}
double X(int j){return sum[j];}
double slop(int j1,int j2){return (Y(j2)-Y(j1))/(X(j2)-X(j1));}
int read()
{
        char c;int tmp=0;
        c=getchar();
        while (c<'0' || c>'9') c=getchar();
        while (c>='0' && c<='9')
        {
            tmp*=10;tmp+=c-'0';
            c=getchar();
        }return tmp;
}
int main()
{
	int ln,n,i,k;sum[0]=ln=0;
	n=read();k=read();
	for (i=1;i<=n;i++) a[i]=read();
	for (i=1;i<=n;i++)
	{
		if (a[i]!=0) a[++ln]=a[i];
		sum[ln]=sum[ln-1]+a[ln];
	}n=ln;t=0; 
	memset(f,0,sizeof(f));
	for (int ii=1;ii<=k;ii++)
	{
		l=1;r=0;
		for (i=ii;i<=n;i++)
		{
			while (l<r && slop(q[r-1],q[r])<slop(q[r],i-1)) r--;
			q[++r]=i-1; 
			while (l<r && slop(q[l],q[l+1])>-sum[i]) l++;
			int j=q[l];
			f[t][i]=f[1-t][j]+(sum[i]-sum[j])*sum[j];
		}t=1-t;
	}
	printf("%lld\n",f[1-t][n]);
	return 0;
}

啊我的代码,,巨慢orzorzorz。。。读优加了也就快了几百ms,然而比起我巨慢的总时间来说简直微不足道。(每次交我都觉得我在卡评测。。。

posted @ 2016-09-26 17:13  OxQ  阅读(367)  评论(0编辑  收藏  举报