[洛谷P2824][题解][HEOI2016/TJOI2016]排序
0.概览
1.解法
每次都排序太麻烦了,我们考虑如何转化。
简化问题:给定一个只由0和1组成的序列,进行相同的操作和查询。
对于01序列的排序,我们可以直接使用线段树区间修改实现(统计出1的个数后把一边赋为1一边赋为0)。
这道简化问题与原题的唯一区别就是原题是一个排列。
众所周知,求答案的题经常可以通过二分转化为较简单的判定,此题也不例外。
我们二分一个答案\(limt\),每次将所有大于它的数置为1,小于它的数置为0。
接下来?接下来就可以用简化问题的方法判定了!
我们只要转化成01序列之后进行操作,最终\(q\)位置上的是1就满足条件。
2.代码
#define N 1000010
int n,m,num[N],ans,q;
struct Input {
int l,r,opt;
}in[N];
struct Node {
int l,r,wei,tag;
}tr[N<<2];
inline void Pushup(int k){
tr[k].wei=tr[ls].wei+tr[rs].wei;
}
void Build(int k,int l,int r,int limt){
tr[k].l=l,tr[k].r=r,tr[k].tag=0;
if(l==r){
tr[k].wei=num[l]>=limt;
return;
}
Build(ls,l,nmid,limt);
Build(rs,nmid+1,r,limt);
Pushup(k);
}
inline void Pushdn(int k){
if(tr[k].tag){
tr[ls].tag=tr[rs].tag=tr[k].tag;
if(tr[k].tag==1){
tr[ls].wei=tmid-tr[k].l+1;
tr[rs].wei=tr[k].r-tmid;
}else tr[ls].wei=tr[rs].wei=0;
tr[k].tag=0;
}
}
void Modify(int k,int l,int r,int num){
if(l<=tr[k].l&&tr[k].r<=r){
tr[k].wei=num*(tr[k].r-tr[k].l+1);
tr[k].tag=num?1:-1;
return;
}
Pushdn(k);
if(l<=tmid)Modify(ls,l,r,num);
if(tmid<r)Modify(rs,l,r,num);
Pushup(k);
}
int Query(int k,int l,int r){
if(l<=tr[k].l&&tr[k].r<=r){
return tr[k].wei;
}
Pushdn(k);
int res=0;
if(l<=tmid)res+=Query(ls,l,r);
if(tmid<r)res+=Query(rs,l,r);
return res;
}
int Queryp(int k,int pos){
if(Thispoint){
return tr[k].wei;
}
Pushdn(k);
if(pos<=tmid)return Queryp(ls,pos);
else return Queryp(rs,pos);
}
bool Check(int limt){
Build(1,1,n,limt);
for(rg int i=1;i<=m;i++){
int cnt=Query(1,in[i].l,in[i].r);
int ll=in[i].l,rr=in[i].r;
if(in[i].opt==0){
Modify(1,ll,rr-cnt,0);
Modify(1,rr-cnt+1,rr,1);
}else {
Modify(1,ll+cnt,rr,0);
Modify(1,ll,ll+cnt-1,1);
}
}
return Queryp(1,q);
}
int main(){
Read(n),Read(m);
for(rg int i=1;i<=n;i++)Read(num[i]);
for(rg int i=1;i<=m;i++){
Read(in[i].opt),Read(in[i].l),Read(in[i].r);
}
Read(q);
int l=1,r=n;
while(l<=r){
if(Check(nmid))ans=nmid,l=nmid+1;
else r=nmid-1;
}
cout<<ans<<endl;
return 0;
}
3.完结撒花
内容来自_ajthreac_的博客(https://www.cnblogs.com/juruoajh/),未经允许,不得转载。