BZOJ 3675 序列分割

Description

小H最近迷上了一个分割序列的游戏。在这个游戏里,小H需要将一个长度为N的非负整数序列分割成\(k+1\)个非空的子序列。为了得到\(k+1\)个子序列,小H将重复进行七次以下的步骤:
\(1.\)小H首先选择一个长度超过1的序列(一开始小H只有一个长度为\(n\)的序列一一也就是一开始得到的整个序列);
\(2.\)选择一个位置,并通过这个位置将这个序列分割成连续的两个非空的新序列。
每次进行上述步骤之后,小H将会得到一定的分数。这个分数为两个新序列中元素和的乘积。小H希望选择一种最佳的分割方案,使得\(k\)轮(次)之后,小H的总得分最大。

Input

输入文件的第一行包含两个整数\(n\)\(k\)\(k+1 \le n\))。
第二行包含\(n\)个非负整数\(a_{1},a_{2},\cdots,a_{n}(0 \le a_{i} \le 10^{4})\),表示一开始小H得到的序列。

Output

一行包含一个整数,为小H可以得到的最大得分。

Sample Input

7 3
4 1 3 4 0 2 3

Sample Output

108

HINT

数据满足\(2 \le n \le 100000,1 \le k \le min(n-1,200)\)

我们做这道题首先要知道一个结论:对于一个划分最终态,它的答案一定是确定的,与划分的顺序没有任何的关系。
证明直接从所求出发,假设第\(i\)块的和为\(sum_{i}\),那么$$ans = \sum_{i=1}^{k+1} \sum_{j=i+1}^{k+1} sum_{i}sum_{k}$$
于是我们不难想到状态\(f_{i,j}\)表示序列前\(i\)个分成\(j\)段的最大值。\(pre_{i}\)表示\(i\)的前缀和,不难想到转移:$$f_{i,j}=f_{k,j-1}+pre_{k}(pre_{i}-pre_{k})$$
然后就是什么单调栈啊,斜率优化上来优化这个dp即可。

讲讲斜率优化怎么搞吧(会的可以无视这段话):
将转移式子化简一下可得:$$f_{i,j}=pre_{i}pre_{k}+f_{k,j-1}-pre_{k}^{2}$$
令$$x = pre_{k},y = f_{k,j-1}-pre_{k}^{2}$$
则$$f_{i,j}=pre_{i}x+y$$
即用一条斜率为\(-pre_{i}\)直线截平面上一堆的点\((x,y)\)得到最大截距。由于\(x\)单调,单调队列维护即可。

#include<cstdio>
#include<cstdlib>
using namespace std;

#define maxn (100010)
#define maxk (210)
typedef long long ll;
int N,K,seq[maxn],head[2],tail[2]; ll pre[maxn],f[2][maxn];
struct node { ll x,y; int ord; }team[2][maxn];

inline bool Cmp(const node &a,const node &b,ll k)
{
	if (a.x == b.x) return true;
	return a.y-b.y<k*(a.x-b.x);
}
inline void poph(int now,ll k,int id)
{
	while (tail[now]-head[now] >= 2&&team[now][head[now]+1].ord<id-1&&Cmp(team[now][head[now]+1],team[now][head[now]+2],k)) ++head[now];
}
inline ll front(int now,ll k) { return -k*team[now][head[now]+1].x+team[now][head[now]+1].y; }
inline bool cmp(const node &a,const node &b,const node &c,const node &d)
{
	if (a.x == b.x) return false;
	if (c.x == d.x) return true;
	return (c.x-d.x)*(a.y-b.y)<(a.x-b.x)*(c.y-d.y);
}
inline void ins(int now,const node &p)
{
	while (tail[now]-head[now] >= 2&&cmp(team[now][tail[now]],team[now][tail[now]-1],p,team[now][tail[now]])) --tail[now];
	team[now][++tail[now]] = p;
}

int main()
{
	freopen("3675.in","r",stdin);
	freopen("3675.out","w",stdout);
	scanf("%d %d",&N,&K);
	for (int i = 1;i <= N;++i) scanf("%d ",seq+i),pre[i] += (ll)seq[i]+pre[i-1];
	for (int j = 1;j <= K+1;++j)
	{
		int p = j&1,q = p^1;
		head[q] = tail[q] = 0;
		for (int i = j;i <= N;++i)
		{
			poph(p,-pre[i],i);
			f[p][i] = front(p,-pre[i]);
			ins(q,(node){pre[i],f[p][i]-pre[i]*pre[i],i});
		}
	}
	printf("%lld\n",f[(K+1)&1][N]);
	fclose(stdin); fclose(stdout);
	return 0;
}
posted @ 2015-04-25 19:57  lmxyy  阅读(142)  评论(0编辑  收藏  举报