题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=3675
思路
首先设
f[i][k]=长度为i的序列,划分了k次
得到的分数,
sum[i]=∑it=1At
,即序列
A
的前缀和
很容易推出DP方程:
变化一下
f[i][k]=max{f[j][k−1]+sum[i]sum[j]−sum[j]2)}
设DP到 f[x][k+1] , f[x][k]比f[y][k] 优( x<y ),则
f[x][k]+sum[i]sum[x]−sum[x]2−f[y][k−1]−sum[i]sum[y]+sum[y]2>0
(f[x][k]−sum[x]2)−(f[y][k]−sum[y]2)sum[y]−sum[x]>sum[i]
因此我们可以维护一个单调队列,保证队首的f是最优的。那么若 q[h]和q[h+1] 连线斜率小于 sum[i] 则说明 q[h+1]比q[h] 优,弹掉 q[h] ,并时刻维护一个斜率单调递增的单调队列
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 110000
using namespace std;
typedef long long int LL;
int q[MAXN],n,m;
LL f[MAXN][2],sum[MAXN];
double getSlope(int x,int y,int now)
{
return (double)((f[x][now]-sum[x]*sum[x])-(f[y][now]-sum[y]*sum[y]))/(sum[y]-sum[x]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&sum[i]);
sum[i]+=sum[i-1];
}
//for(int i=1;i<=n;i++) f[i][1]=sum[i];
int now=1;
for(int j=1;j<=m;j++) //划分j次
{
now^=1;
int h=1,t=1;
q[h]=0;
for(int i=1;i<=n;i++) //前i个元素
{
while(h<t&&getSlope(q[h],q[h+1],now^1)<sum[i]) h++;
f[i][now]=f[q[h]][now^1]+sum[q[h]]*(sum[i]-sum[q[h]]);
while(h<t&&getSlope(q[t-1],q[t],now^1)>getSlope(q[t],i,now^1)) t--;
q[++t]=i;
}
}
printf("%lld\n",f[n][now]);
return 0;
}