首先发现如果切同样的地方,先切后切都一样,即操作顺序没影响,这样满足无后效性就可以DP了
记 f[i][j] 表示前i的序列,切j次最大答案
于是得到裸方程: f[i][j]=max(f[k][j−1]+sum[k]∗(sum[i]−sum[k]))(k<i)
复杂度 O(n2k) 太大,进行斜率优化
假设g < k,k比g优
f[k][j−1]+sum[k]∗(sum[i]−sum[k])−f[g][j−1]−sum[g]∗(sum[i]−sum[g])>0
整理
(f[k][j−1]−sum[k]2)−(f[g][j−1]−sum[g]2)sum[k]−sum[g]>−sum[i]
斜率 sum[i] 单调递减,那么维护上凸壳即可
被细节坑死!代码里的分母是负的,忘记变不等号,调了半天。。。
队列里面只装下标,装带x,y的结构体会强行TLE
多余的维要压掉
#include<cstdio>
#include<cstring>
#define MAXN 100005
#define MAXK 205
using namespace std;
long long sum[MAXN], f[MAXN][2],y[MAXN],x[MAXN];
int from[MAXN][MAXK],q[MAXN];
int in(){
char ch;int aa;
while(ch=getchar(),ch<'0'||ch>'9');aa=ch-'0';
while(ch=getchar(),ch>='0'&&ch<='9')aa=aa*10+ch-'0';return aa;
}
int main()
{
int n=in(), k=in();
for(int i = 1; i <= n; i++)
sum[i]+=sum[i-1]+in();
int cur = 1, last=0;
for(int j = 1; j <= k; j++)
{
int head=0,tail=1;
cur^=1;last^=1;
x[0]=sum[j];
y[0]=f[j][last]-sum[j]*sum[j];
q[0]=j;
for(int i = j+1; i <= n; i++)
{
//正负
while(tail-head>1 && (y[head]-y[head+1])<=(-sum[i])*(x[head]-x[head+1]))head++;
f[i][cur]=y[head]+sum[i]*x[head];
long long nowx=sum[i], nowy=f[i][last]-sum[i]*sum[i];
from[i][j]=q[head];
while(tail-head>1 && (x[tail-1]-x[tail-2])*(nowy-y[tail-2])-(nowx-x[tail-2])*(y[tail-1]-y[tail-2])>=0)tail--;
x[tail]=nowx;
y[tail]=nowy;
q[tail++]=i;
}
}
printf("%lld\n",f[n][cur]);
for(int i = k; i; i--)
{
n=from[n][i];
printf("%d ",n);
}
return 0;
}