LGP2824 [HETS 2016||TJTS 2016] 排序 学习笔记

LGP2824 [HETS 2016||TJTS 2016] 排序 学习笔记

Luogu Link

题意简述

给出一个长为 n 的排列 A,对其进行 m 次局部排序,排序分为两种:

  • 0 l r表示将区间 [l,r] 的数字按升序排序;
  • 1 l r表示将区间 [l,r] 的数字按降序排序。
    这里的区间指的是下标而非值。
    最后一次询问,询问上述所有操作依次完成后 AQ 的值。

做法解析

发现只有一次询问,而且还是在所有操作之后。套路地考虑二分答案 mid:对于每次二分,将所有大于等于 mid 的元素都设为 1,小于它的元素设为 0
这样子排序操作因为只有 01,就从 N\logN 变成 \logN 的区间求和和区间赋值了,显然用线段树维护。如果最后目标是 1 就说明实际答案大于等于 mid,收缩 l,反之亦然。

然后做完了。时间复杂度 O(Mlog2N)

代码实现

#include <bits/stdc++.h>
using namespace std;
namespace obasic{
    template <typename _T>
    void readi(_T &x){
        _T k=1;x=0;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')k=-1;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
        x*=k;return;
    }
    template <typename _T>
    void writi(_T x){
        if(x<0)putchar('-'),x=-x;
        if(x>9)writi(x/10);
        putchar(x%10+'0');
    }
};
using namespace obasic;
const int MaxN=1e5+5;
int N,M,A[MaxN],Q;
struct oper{int t,l,r;}P[MaxN];
struct SegTree{
    int cl[MaxN<<2],cr[MaxN<<2],cmid[MaxN<<2];
    int sum[MaxN<<2],tag[MaxN<<2];
    int ls(int p){return p<<1;}
    int rs(int p){return (p<<1)|1;}
    void ainit(int p,int l,int r){
        cl[p]=l,cr[p]=r,cmid[p]=(l+r)>>1;if(l==r)return;
        ainit(ls(p),l,cmid[p]);ainit(rs(p),cmid[p]+1,r);
    }
    void pushup(int p){
        sum[p]=sum[ls(p)]+sum[rs(p)];
    }
    void build(int p,int v){
        tag[p]=-1;
        if(cl[p]==cr[p]){sum[p]=(A[cl[p]]>=v?1:0);return;}
        build(ls(p),v),build(rs(p),v);pushup(p);
    }
    void maketag(int p,int v){
        sum[p]=v*(cr[p]-cl[p]+1),tag[p]=v;
    }
    void pushdown(int p){
        if(tag[p]==-1||cl[p]==cr[p])return;
        maketag(ls(p),tag[p]);
        maketag(rs(p),tag[p]);
        tag[p]=-1;
    }
    int getsum(int p,int dl,int dr){
        if(dl<=cl[p]&&cr[p]<=dr)return sum[p];
        pushdown(p);int res=0;
        if(dl<=cmid[p])res+=getsum(ls(p),dl,dr);
        if(dr>cmid[p])res+=getsum(rs(p),dl,dr);
        pushup(p);return res;
    }
    void update(int p,int dl,int dr,int v){
        if(dl<=cl[p]&&cr[p]<=dr){maketag(p,v);return;}
        pushdown(p);
        if(dl<=cmid[p])update(ls(p),dl,dr,v);
        if(dr>cmid[p])update(rs(p),dl,dr,v);
        pushup(p);
    }
}SegTr;
bool check(int lim){
    SegTr.build(1,lim);
    for(int i=1;i<=M;i++){
        auto [pt,pl,pr]=P[i];
        int cnt=SegTr.getsum(1,pl,pr);
        if(cnt==pr-pl+1)continue;
        if(pt==0){
            SegTr.update(1,pl,pr-cnt,0);
            SegTr.update(1,pr-cnt+1,pr,1);
        }
        if(pt==1){
            SegTr.update(1,pl,pl+cnt-1,1);
            SegTr.update(1,pl+cnt,pr,0);
        }
    }
    return SegTr.getsum(1,Q,Q);
}
int main(){
    readi(N),readi(M);SegTr.ainit(1,1,N);
    for(int i=1;i<=N;i++)readi(A[i]);
    for(int i=1;i<=M;i++)readi(P[i].t),readi(P[i].l),readi(P[i].r);
    readi(Q);int bl=1,br=N,bmid,ans;
    while(bl<=br){
        bmid=(bl+br)>>1;
        if(check(bmid))ans=bmid,bl=bmid+1;
        else br=bmid-1;
    }
    writi(ans);
    return 0;
}

反思总结

如果题目问进行了一堆比较大小相关操作后只跟了一次询问,考虑二分这个询问的结果,然后将大于等于它的东西设为 1,小于它的东西设为 0,抽象为 01 后很多操作都可以在更好的复杂度内完成,如果最后目标是 1 就说明实际答案大于等于 mid,收缩 l,反之亦然。

另外如果线段树要修改或查询由比较活跃的变量决定的区间的话,注意判空!!!

posted @   矞龙OrinLoong  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示