CF573E-Bear and Bowling【dp,平衡树】

1|0正题

题目链接:https://www.luogu.com.cn/problem/CF573E


1|1题目大意

给出一个长度为n的序列a,求它的一个子序列b,要求最大化

i=1|b|bi×i

1n105,|ai|107


1|2解题思路

首先我们考虑最暴力的dp,设fi,j表示到现在到a的第i个,然后选择了j个时的最大答案,那么我们有

fi,j=max{fi1,j,fi1,j1+bi×j}

然后发现这个dp很难进行维护,我们尝试找下性质。
然后我没找到去看题解发现确实是性质题,对于一个i,如果fi,jfi,j1转移过来,那么fi,j+1也一定是从fi1,j转移过来的。

证明的话可以看这篇大佬的博客:https://www.luogu.com.cn/blog/Mrsrz/solution-cf573e

所以我们可以用一个平衡树去维护每个idp值,然后我们就只需要二分出两个转移方式的中转点k,然后对于前面的我们不变,对于后面的我们在区间前插入一个fi,k1,然后就是一个区间加等差序列的操作,用懒标记维护即可。

时间复杂度:O(nlog2n)

如果在平衡树上二分能做到O(nlogn)

当然还有另一种做法,考虑一个一个插入答案,能够证明不停插入会使得当前贡献最大的数也是最优的。

那么我们就只需要用分块维护每一个数的新贡献就好了。

时间复杂度:O(nn)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define ll long long using namespace std; const ll N=1e5+10; ll n,cnt,rt,t[N][2],dat[N],siz[N]; ll w[N],lazy[N],lazy2[N],ans; ll Newp(ll val){ w[++cnt]=val;dat[cnt]=rand(); siz[cnt]=1;return cnt; } ll Cpy(ll x) {return Newp(w[x]);} void Update(ll x,ll val,ll dr){ w[x]+=val*(siz[t[x][0]]+dr); lazy[x]+=val*dr;lazy2[x]+=val; } void PushDown(ll x){ if(lazy[x]){ if(t[x][0])w[t[x][0]]+=lazy[x],lazy[t[x][0]]+=lazy[x]; if(t[x][1])w[t[x][1]]+=lazy[x],lazy[t[x][1]]+=lazy[x]; lazy[x]=0; } if(lazy2[x]){ if(t[x][0])Update(t[x][0],lazy2[x],0); if(t[x][1])Update(t[x][1],lazy2[x],siz[t[x][0]]+1); lazy2[x]=0; } return; } void PushUp(ll x){siz[x]=siz[t[x][0]]+siz[t[x][1]]+1;return;} void Split(ll &x,ll &y,ll p,ll val){ if(!p){x=y=0;return;}PushDown(p); if(siz[t[p][0]]<=val)x=p,Split(t[x][1],y,t[p][1],val-siz[t[p][0]]-1); else y=p,Split(x,t[y][0],t[p][0],val); PushUp(p); } ll Merge(ll x,ll y){ if(!x||!y)return x|y; PushDown(x);PushDown(y); if(dat[x]<dat[y]){ t[x][1]=Merge(t[x][1],y); PushUp(x);return x; } else{ t[y][0]=Merge(x,t[y][0]); PushUp(y);return y; } } ll GetVal(ll &rt,ll pos){ ll x,y,z; Split(x,z,rt,pos); Split(x,y,x,pos-1); ll ans=w[y]; x=Merge(x,y); rt=Merge(x,z); return ans; } void GetAns(ll x){ if(!x)return;PushDown(x); ans=max(ans,w[x]); GetAns(t[x][0]);GetAns(t[x][1]); return; } bool check(ll x,ll w){ ll a=GetVal(rt,x); ll b=GetVal(rt,x-1); return a>b+x*w; } signed main() { scanf("%lld",&n);rt=Newp(0); rt=Merge(rt,Newp(-1e18)); for(ll i=1,k;i<=n;i++){ scanf("%lld",&k); ll l=1,r=i; while(l<=r){ ll mid=(l+r)>>1; if(check(mid,k))l=mid+1; else r=mid-1; } ll x,y,z,d; Split(x,z,rt,l-1); Split(z,d,z,n-1); Split(x,y,x,l-2); d=Cpy(y); z=Merge(d,z); Update(z,k,l); rt=x;rt=Merge(rt,y); rt=Merge(rt,z); } GetAns(rt); printf("%lld\n",ans); return 0; }```

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/16004273.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2021-03-14 P4480-[BJWC2018]餐巾计划问题【三分,贪心】
点击右上角即可分享
微信分享提示