[HEOI2016/TJOI2016]排序

题目链接

1|0Solution

考虑离线回答这个问题,由于只问我们第 q 位,我们很自然想到枚举一个阈值,将大于等于它的变成 1,小于它的变成 0,然后对这个 01 序列进行排序操作。有单调性,举个栗子:

假设知道一个排完序后的序列 3,1,2,4,5,现询问第 3 个位置。
若选 1 作为阈值,排序好的 01 序列为 1,1,1,1,1,第 3 个位置为 1
若选 2 作为阈值,排序好的 01 序列为 1,0,1,1,1,第 3 个位置为 1
若选 3 作为阈值,排序好的 01 序列为 1,0,0,1,1,第 3 个位置为 0
若选 4 作为阈值,排序好的 01 序列为 0,0,0,1,1,第 3 个位置为 0
若选 5 作为阈值,排序好的 01 序列为 0,0,0,0,1,第 3 个位置为 0

可以发现,最大的满足第 q 个位置为 1 的阈值就是答案。若阈值偏小,则该位置为 1;若阈值偏大,则该位置为 0。所以满足单调性,二分答案即可。

还有一个问题,如何快速对 01 序列排序。我们只需要知道这个序列中 1 的个数,再根据升序还是降序,决定这几个 1 放最前还是最后。用线段树进行区间求和以及区间修改即可。
注意特判全是 01 的情况,不然区间修改会 RE。

2|0Code

#include<bits/stdc++.h> #define ls p*2 #define rs p*2+1 using namespace std; void read(int &x) { char ch=getchar(); int r=0,w=1; while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar(); while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar(); x=r*w; } const int N=1e5+7; int a[N],n,m,q,b[N],sum[N*4],lazy[N*4],size[N*4],ans; struct sbt { int op,l,r; }qt[N]; void update(int p) { sum[p]=sum[ls]+sum[rs]; size[p]=size[ls]+size[rs]; } void pushdown(int p) { lazy[ls]=lazy[p]; sum[ls]=size[ls]*lazy[p]; lazy[rs]=lazy[p]; sum[rs]=size[rs]*lazy[p]; lazy[p]=-1; } void build(int p,int l,int r) { lazy[p]=-1; if(l==r) { sum[p]=b[l]; size[p]=1; return; } int mid=l+r>>1; build(ls,l,mid); build(rs,mid+1,r); update(p); } void change(int p,int l,int r,int x,int y,int k) { if(x<=l&&r<=y) { lazy[p]=k; sum[p]=size[p]*k; return; } if(lazy[p]!=-1)pushdown(p); int mid=l+r>>1; if(x<=mid)change(ls,l,mid,x,y,k); if(y>mid)change(rs,mid+1,r,x,y,k); update(p); } void query(int p,int l,int r,int x,int y) { if(x<=l&&r<=y) { ans+=sum[p]; return; } if(lazy[p]!=-1)pushdown(p); int mid=l+r>>1; if(x<=mid)query(ls,l,mid,x,y); if(y>mid)query(rs,mid+1,r,x,y); } bool check(int x) { for(int i=1;i<=n;i++) b[i]=a[i]>=x?1:0; build(1,1,n); for(int i=1;i<=m;i++) { int op=qt[i].op,l=qt[i].l,r=qt[i].r; ans=0; query(1,1,n,l,r); if(ans==r-l+1||ans==0)continue; if(!op) { change(1,1,n,l,r-ans,0); change(1,1,n,r-ans+1,r,1); } else { change(1,1,n,l,l+ans-1,1); change(1,1,n,l+ans,r,0); } } ans=0; query(1,1,n,q,q); return ans==1; } int main() { read(n);read(m); for(int i=1;i<=n;i++) read(a[i]); for(int i=1;i<=m;i++) read(qt[i].op),read(qt[i].l),read(qt[i].r); read(q); int l=1,r=n,ans; while(l<=r) { int mid=l+r>>1; if(check(mid))ans=mid,l=mid+1; else r=mid-1; } cout<<ans; return 0; }

__EOF__

本文作者JMartin
本文链接https://www.cnblogs.com/LAK666/p/16602800.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Epoch_L  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示