给定一个长 的序列 ,从 中选择一个子序列 ,使得 最大,输出这个最大值。
,。
考虑一个显然的 dp,设 表示 中选了 个的最大 ,则有转移:
有一个神奇的结论,存在 满足:
具体证明有点复杂,大概就是这个东西的本质是一个贪心的优化。
那么用平衡树动态维护 的差分数组即可,时间复杂度 。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
using namespace std;
const int S=100005;
struct node
{
int ls,rs,w;
int siz;
long long val,tag;
};
node tr[S];
int trcnt;
int nnde(long long x)
{
tr[++trcnt]=(node){0,0,rand(),1,x,0};
return trcnt;
}
void upd(int u)
{
int ls=tr[u].ls,rs=tr[u].rs;
tr[u].siz=tr[ls].siz+tr[rs].siz+1;
}
void dwntag(int u)
{
int ls=tr[u].ls,rs=tr[u].rs;
tr[ls].val+=tr[u].tag,tr[ls].tag+=tr[u].tag;
tr[rs].val+=tr[u].tag,tr[rs].tag+=tr[u].tag;
tr[u].tag=0;
}
void split(int u,long long val,int siz,int &x,int &y)
{
if(u==0) return x=y=0,void();
dwntag(u);
int &ls=tr[u].ls,&rs=tr[u].rs;
int sz=tr[ls].siz+siz+1;
if(tr[u].val>val*sz) split(rs,val,sz,rs,y),x=u;
else split(ls,val,siz,x,ls),y=u;
upd(u);
}
void merge(int x,int y,int &u)
{
if(x==0||y==0) return u=x+y,void();
dwntag(x),dwntag(y);
int &xrs=tr[x].rs,&yls=tr[y].ls;
if(tr[x].w<tr[y].w) merge(xrs,y,xrs),u=x;
else merge(x,yls,yls),u=y;
upd(u);
}
long long getans(int u,long long &sum)
{
if(u==0) return 0;
dwntag(u);
long long res=getans(tr[u].ls,sum);
res=max(res,sum+=tr[u].val);
res=max(res,getans(tr[u].rs,sum));
return res;
}
int n,rt;
int main()
{
srand(time(NULL));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
long long x;
scanf("%lld",&x);
int rx,ry;
split(rt,x,0,rx,ry);
int k=tr[rx].siz+1;
int pre=nnde(x*k);
tr[ry].val+=x,tr[ry].tag+=x;
merge(rx,pre,rt);
merge(rt,ry,rt);
}
long long sum=0;
printf("%lld\n",getans(rt,sum));
return 0;
}