感谢Orz大佬的斜率优化讲解
因为得分跟顺序无关,所以可以dp
令
fi,j
f
i
,
j
表示第
i
i
次在处分割的最大得分,
si
s
i
为
∑j=1j≤iaj
∑
j
=
1
j
≤
i
a
j
,则有
状态 t t 比优,当且仅当
令 gj=fi−1,j g j = f i − 1 , j ,拆开括号,有
再令 pj=gj−sj∗sn p j = g j − s j ∗ s n ,那么
这个东西就是斜率啊!
将每个状态表示为 (pi,si) ( p i , s i ) 的点对,维护一个下凸壳,因为 s s <script type="math/tex" id="MathJax-Element-33">s</script>是单调的,所以用单调队列搞搞就可以了
代码如下:
#include<algorithm>
#include<ctype.h>
#include<cstdio>
#define INF 2147483647000000ll
#define N 100050
using namespace std;
inline int read()
{
int x=0,f=1;char c;
do c=getchar(),f=c=='-'?-1:f; while(!isdigit(c));
do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
return x*f;
}
typedef long long LL;
int n,k,l,r;
LL s[N],f[N],g[N],ans;
struct Point
{
LL x,y;
Point(){}
Point(LL _,LL __):x(_),y(__){}
double operator * (const Point &b)///算斜率
{
if(x==b.x) return y>b.y ?-INF:INF;
return 1.0*(b.y-y)/(b.x-x);
}
}q[N];
inline LL calc(int k)
{
return g[k]-s[k]*s[n];
}
inline LL GetAns(int k)
{
while(l<r && q[l].x+q[l].y*s[k]<q[l+1].x+q[l+1].y*s[k]) l++;
return q[l].x-s[k]*s[k]+s[k]*s[n]+q[l].y*s[k];
}
inline void Add(Point k)
{
while(l<r && q[r-1]*q[r]>q[r]*k) r--;
q[++r]=k;
}
main()
{
n=read();k=read();
for(int i=1;i<=n;i++)
s[i]=read()+s[i-1];
for(int i=1;i<=k;i++)
{
l=r=1;
for(int j=1;j<=n;j++)
{
f[j]=GetAns(j);
Add(Point(calc(j),s[j]));
}
for(int j=1;j<=n;j++)
g[j]=f[j];
}
for(int i=1;i<n;i++)
ans=max(ans,g[i]);
printf("%lld",ans);
return 0;
}