洛谷题目传送门
有一个贪心策略是维护下一个数字选择某一个位置的最优解,然和每次选择最优的来转移,把他选了,然后更新其他位置的代价
设
t
i
t_i
ti表示
i
i
i之前选的数字的个数,
b
i
b_i
bi表示
i
i
i之后选的数字的
a
a
a值之和
那么在
i
i
i选择的代价是
a
i
×
t
i
+
b
i
a_i\times t_i+b_i
ai×ti+bi,我们就是要最大化这个式子
考虑用分块维护这个东西,然后在每个块内建立一个凸包,然后就是斜率优化的套路
考虑设
f
i
=
a
i
×
t
i
+
b
i
f_i=a_i\times t_i+b_i
fi=ai×ti+bi,则
b
i
=
−
a
i
×
t
i
+
f
i
b_i=-a_i\times t_i+f_i
bi=−ai×ti+fi
相当于是用一条斜率为
t
i
t_i
ti的线,截若干形容
(
−
a
i
,
b
i
)
(-a_i,b_i)
(−ai,bi)的点
为了方便维护,我们让整个块的
t
i
t_i
ti相同,然后随着选的数的增加,
t
t
t的值会增加,所以最优价的位置不会向前移动
当选出一个数是x时,之前块的所有元素的
b
i
b_i
bi都会加上
a
x
a_x
ax,之后所有块的
t
i
t_i
ti都会加1
因为是整块加减,所以可以打标记,因为整体加相当于将所有点平移,而斜率不会发生改变
所以不会影响斜率的查询
至于
x
x
x这个块,因为不是整体平移,斜率会改变,所以把整个块重构即可
总复杂度
O
(
n
n
)
O(n\sqrt n)
O(nn)
略微卡常
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF = 1e15;
typedef double db;
const LL N = 2e5+7;
const LL Q = 500;
LL que[Q][Q],Head[Q],Tail[Q],St[Q],Ed[Q],Pos[N];
LL A[N],B[N],t[Q],Add[Q];
LL p[N];
LL len,cnt=0;
LL n;
LL cmp(LL a,LL b)
{
return A[a]<A[b];
}
db slope(LL x,LL y)
{
if(A[y]==A[x])
{
if(B[x]==B[y]) return 1;
if(B[x]<B[y]) return INF;
return -INF;
}
return (B[y]-B[x])/(A[y]-A[x]);
}
void Build(LL x)
{
Head[x]=1;
Tail[x]=0;
LL l=St[x],r=Ed[x];
for(LL o=l;o<=r;o++)
{
LL i=p[o];
B[i]=B[i]+Add[x]+t[x]*A[i];
}
Add[x]=0;
t[x]=0;
for(LL o=l;o<=r;o++)
{
LL i=p[o];
while(Head[x]<Tail[x]&&slope(que[x][Tail[x]-1],que[x][Tail[x]])<=slope(que[x][Tail[x]],i)) Tail[x]--;
que[x][++Tail[x]]=i;
}
// cout<<x<<": "<<endl;
// for(LL i=Head[x];i<=Tail[x];i++)
// cout<<que[x][i]<<'-'<<A[que[x][i]]<<'-'<<B[que[x][i]]<<' ';
// cout<<endl;
// cout<<"---------"<<endl;
}
pair<LL,LL> Ask(LL x)
{
while(Head[x]<Tail[x]&&slope(que[x][Head[x]],que[x][Head[x]+1])>=-t[x]) ++Head[x];
if(Head[x]>Tail[x]) return make_pair(-INF,0);
// cout<<que[x][Head[x]]<<' '<<Add[x]<<' '<<t[x]<<' '<<A[que[x][Head[x]]]<<endl;
return make_pair(B[que[x][Head[x]]]+Add[x]+t[x]*A[que[x][Head[x]]],que[x][Head[x]]);
}
inline LL read()
{
LL x=0,t=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
int main()
{
// freopen("JOI.in","r",stdin);
// freopen("JOI.out","w",stdout);
scanf("%lld",&n);
len=sqrt(n);
if(len==0) len=1;
if(n==1) len=1;
for(LL i=1;i<=n;i++)
{
A[i]=read();
B[i]=A[i];
p[i]=i;
Pos[i]=(i-1)/len+1;
if(!St[Pos[i]]) St[Pos[i]]=i;
Ed[Pos[i]]=max(Ed[Pos[i]],i);
cnt=max(cnt,Pos[i]);
}
if(n==1)
{
cout<<max(0ll,A[1]);
return 0;
}
for(LL i=1;i<=cnt;i++)
{
sort(p+St[i],p+Ed[i]+1,cmp);
Build(i);
}
LL Ans=0,sum=0;
for(LL i=1;i<=n;i++)
{
pair<LL,LL> ans(-INF,0);
for(LL x=1;x<=cnt;x++)
{
pair<LL,LL> pos=Ask(x);
if(pos.first>=ans.first) ans=pos;
}
// cout<<ans.first<<' '<<ans.second<<endl;
sum+=ans.first;
Ans=max(Ans,sum);
LL r=ans.second,R=Pos[r];
for(LL x=1;x<R;x++) Add[x]+=A[r];
for(LL x=R+1;x<=cnt;x++) t[x]++;
for(LL x=St[R];x<r;x++) B[x]+=A[r];
for(LL x=r+1;x<=Ed[R];x++) B[x]+=A[x];
B[r]=-INF;
Build(R);
}
cout<<Ans;
}