UOJ#749-[UNR #6]稳健型选手【贪心,分治,主席树】

1|0正题

题目链接:https://uoj.ac/problem/749


1|1题目大意

如果有序列a,你每次取走一个数字后然后这个序列最前面的数字会被别人取走,直到序列为空。此时f(a)表示你最大能取走的权值和。

给出一个长度为n的序列aq次询问区间[l,r],求f(alr)

1n,q2×105,1ai109


1|2解题思路

考虑一下最优的取法,我们的限制其实相当于前2i1个数之中不能取走超过i个数。

一个暴力的贪心想法是不停往前走,用一个堆维护现在取了的数,走到不是2i1时我们考虑是否拿这个ai替代堆里最小的数,走到2i1时直接丢进堆里就行了。

或者反过来更简单,走到一个数就丢进堆里,遇到2i1时直接取堆中最大的数就好了。

我们现在能处理左或右端点固定的所有答案了,我们考虑分治,问题在于怎么去合并两个区间的答案。

为了防止大量的分类讨论,对于长度为奇数的区间询问,我们可以考虑去掉区间的末尾,因为这个位置肯定会取到。

然后我们考虑怎么合并区间,显然是前面一些选了的数字变成不选,后面一些不选的数字变成选了,显然是前面拿小的,后面拿大的。

我们考虑在分治的时候对于区间[L,mid,R],对于[L,mid]部分,我们用主席树维护选了的数字,对于[mid+1,R]部分,我们用主席树维护没选的数字,然后对于一个询问,二分一个k,然后左边的取出前k小,右边的取出前k大进行比较即可。

时间复杂度:O(nlog2n)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define ll long long using namespace std; const ll N=2e5+10,M=N<<5; ll n,m,cnt,a[N],b[N],l[N],r[N],ans[N],s[N],rt[N]; priority_queue<ll> q; struct SegTree{ ll cnt,w[M],c[M],ls[M],rs[M]; ll Change(ll x,ll L,ll R,ll pos,ll val){ ll p=++cnt;w[p]=w[x]+val;c[p]=c[x]+val*b[pos]; if(L==R)return p;ll mid=(L+R)>>1; if(pos<=mid)ls[p]=Change(ls[x],L,mid,pos,val),rs[p]=rs[x]; else rs[p]=Change(rs[x],mid+1,R,pos,val),ls[p]=ls[x]; return p; } ll Find(ll x,ll L,ll R,ll k){ if(L==R)return L;ll mid=(L+R)>>1; if(w[ls[x]]>=k)return Find(ls[x],L,mid,k); return Find(rs[x],mid+1,R,k-w[ls[x]]); } ll Ask(ll x,ll L,ll R,ll k){ if(!k)return 0; if(L==R)return b[L]*k;ll mid=(L+R)>>1; if(w[ls[x]]>=k)return Ask(ls[x],L,mid,k); return Ask(rs[x],mid+1,R,k-w[ls[x]])+c[ls[x]]; } }T; bool cmp(ll x,ll y){return l[x]<l[y];} void solve(ll L,ll R,vector<ll> &now){ if(L==R)return; ll mid=(L+R)>>1; vector<ll> pl,pr,p; for(ll i=0;i<now.size();i++) if(l[now[i]]<=mid&&r[now[i]]>mid)p.push_back(now[i]); else if(l[now[i]]<=mid)pl.push_back(now[i]); else pr.push_back(now[i]); solve(L,mid,pl);solve(mid+1,R,pr); if(!p.size())return; for(ll g=0;g<2;g++){ T.cnt=rt[mid]=s[mid]=0; for(ll i=mid+1;i<=R;i++){ if(((i-mid)&1)==g)q.push(-a[i]),s[i]=s[i-1]+b[a[i]],rt[i]=rt[i-1]; else{ if(q.size()&&a[i]>-q.top()){ s[i]=s[i-1]-b[-q.top()]+b[a[i]]; rt[i]=T.Change(rt[i-1],1,cnt,-q.top(),1); q.pop();q.push(-a[i]); } else rt[i]=T.Change(rt[i-1],1,cnt,a[i],1),s[i]=s[i-1]; } } while(!q.empty())q.pop(); for(ll i=mid;i>=L;i--){ q.push(a[i]); if(((mid-i)&1)==g){ rt[i]=T.Change((i==mid)?0:rt[i+1],1,cnt,q.top(),1); s[i]=((i!=mid)?s[i+1]:0)+b[q.top()];q.pop(); } else if(i!=mid)rt[i]=rt[i+1],s[i]=s[i+1]; } while(!q.empty())q.pop(); for(ll i=0;i<p.size();i++){ if(((mid-l[p[i]])&1)!=g)continue; ll X=T.w[rt[l[p[i]]]],Y=T.w[rt[r[p[i]]]]; ll _l=1,_r=min(X,Y); if(p[i]==2) i++,i--; while(_l<=_r){ ll _mid=(_l+_r)>>1; if(T.Find(rt[l[p[i]]],1,cnt,_mid)<T.Find(rt[r[p[i]]],1,cnt,Y-_mid+1)) _l=_mid+1; else _r=_mid-1; } ans[p[i]]+=s[l[p[i]]]-T.Ask(rt[l[p[i]]],1,cnt,_r); ans[p[i]]+=s[r[p[i]]]+T.Ask(rt[r[p[i]]],1,cnt,Y)-T.Ask(rt[r[p[i]]],1,cnt,Y-_r); } } return; } signed main() { scanf("%lld%lld",&n,&m); for(ll i=1;i<=n;i++) scanf("%lld",&a[i]),b[i]=a[i]; sort(b+1,b+1+n);cnt=unique(b+1,b+1+n)-b-1; for(ll i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+cnt,a[i])-b; vector<ll> v; for(ll i=1;i<=m;i++){ scanf("%lld%lld",&l[i],&r[i]);v.push_back(i); if((r[i]-l[i]+1)&1)ans[i]+=b[a[r[i]]],r[i]--; } sort(v.begin(),v.end()); solve(1,n,v); for(ll i=1;i<=m;i++)printf("%lld\n",ans[i]); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/16564143.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(106)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2021-08-08 P4233-射命丸文的笔记【NTT,多项式求逆】
2021-08-08 bzoj#4722-由乃【倍增,抽屉原理,bitset】
2021-08-08 CF835E-The penguin‘s game【交互】
2021-08-08 bzoj#4423-[AMPPZ2013]Bytehattan【并查集】
2021-08-08 P4780-Phi的反函数【dfs】
点击右上角即可分享
微信分享提示