CF573E 题解
思路
下文中称
首先有一个显然的 dp:设
还有一个贪心:每一次选择对当前序列贡献最大的点
引理:在这个贪心策略下,若
证明:假设引理不成立,当前是第一次违背引理的时候,当前选了
接下来考虑证明贪心的正确性。假设贪心不成立,之前选的位置集合
考虑分类讨论:
- 若
B 中有在x 前面的元素,设在x 前面且最靠后的元素为y 。根据引理,a_y \le a_x 。而且,因为x 与y 在B\bigcup{x} 集合中是相邻的,所以两者的cnt_B,sum_B 均相等。所以a_x \cdot cnt_B + sum_B \ge a_y \cdot cnt_B+sum_B ,因此x 对B 集合的贡献比y 对B 集合的贡献大。 - 若
B 中没有在x 前面的元素,则此时cnt_B=0 。设在x 后面的第一个元素为y ,那么同理,两者的cnt_B,sum_B 均相等且cnt_B=0 。因此x 对B 集合的贡献与y 对B 集合的贡献相等。
综上,
考虑回到 dp 上来。有一个结论:对于
这个结论可以用贪心的正确性来证明。考虑用贪心选择前
接下来考虑如何维护
但是更好的方法是维护差分数组。这样只需要维护区间加的操作,而且细节特别少。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
mt19937 rnd(time(0));
int n; int a[500010];
int tot=0,root=0;
struct Fhq_tree
{
int x,pri,siz;
int lson,rson;
int tag;
} tree[500010];
int get_new(int x)
{
tree[++tot]={x,(int)rnd(),1,0,0,0};
return tot;
}
void push_up(int now)
{
tree[now].siz=tree[tree[now].lson].siz+tree[tree[now].rson].siz+1;
}
void push_down(int now)
{
tree[tree[now].lson].x+=tree[now].tag,tree[tree[now].lson].tag+=tree[now].tag;
tree[tree[now].rson].x+=tree[now].tag,tree[tree[now].rson].tag+=tree[now].tag;
tree[now].tag=0;
}
void split(int now,int x,int siz,int &l,int &r)
{
if(now==0) return l=r=0,void();
push_down(now);
int gtsiz=siz+tree[tree[now].lson].siz+1;
if(tree[now].x>x*gtsiz) l=now,split(tree[now].rson,x,gtsiz,tree[now].rson,r);
else r=now,split(tree[now].lson,x,siz,l,tree[now].lson);
push_up(now);
}
int merge(int l,int r)
{
if(l==0 || r==0) return l+r;
push_down(l),push_down(r);
if(tree[l].pri<tree[r].pri)
{
tree[l].rson=merge(tree[l].rson,r);
return push_up(l),l;
}
else
{
tree[r].lson=merge(l,tree[r].lson);
return push_up(r),r;
}
}
int sum=0,ans=0;
void get_ans(int now)
{
push_down(now);
if(tree[now].lson!=0) get_ans(tree[now].lson);
sum+=tree[now].x,ans=max(ans,sum);
if(tree[now].rson!=0) get_ans(tree[now].rson);
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0);
cin>>n;
for(int i=1; i<=n; ++i)
{
cin>>a[i];
int l,r; split(root,a[i],0,l,r);
tree[r].x+=a[i],tree[r].tag+=a[i];
int k=tree[l].siz+1;
root=merge(merge(l,get_new(a[i]*k)),r);
}
get_ans(root); cout<<ans;
return 0;
}