[TJOI2016]排序

本文讨论了TJOI2016中的一道排序题目,由于只有一个询问,无法使用常规的O(logn)排序算法。通过将问题转换为01串排序,利用线段树在O(nlog2n)的时间复杂度内解决,二分查找找到满足条件的最大值。暴力桶排也是可行解法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、题目

点此看题

二、解法

其实本题的突破口在只有一个询问上,因为没有排序算法能达到 O ( log ⁡ n ) O(\log n) O(logn)
考虑 log ⁡ \log log的复杂度排序,发现它只能对 01 01 01串进行排序,我们尝试把问题转化为 01 01 01串的排序。
如果我们有一个分界值 x x x,那么小于 x x x的是 0 0 0,否则是 1 1 1,排序可以用线段树实现,维护 0 0 0的个数,支持区间赋值。这样我们可以得到一个关于大小的描述,我们看最后 q q q位是否为 1 1 1,如果为 1 1 1,就可以确定 a [ q ] ≤ x a[q]\leq x a[q]x,所以我们二分 x x x,找到一个能使 q q q位为 1 1 1的最大的 x x x,即为答案。
时间复杂度 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)

#include <cstdio>
#define Len(i) (tr[i].r-tr[i].l+1)
const int MAXN = 100005;
int read()
{
    int x=0,flag=1;char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^'0'),c=getchar();
    return x*flag;
}
int n,m,q,ans,a[MAXN];
struct node
{
    int op,l,r;
}s[MAXN];
struct tree
{
    int l,r,sum,lazy;//1->0 2->1
}tr[MAXN*4];
void build(int i,int l,int r)
{
    tr[i]=tree{l,r,0,0};
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
}
void down(int i)
{
    if(tr[i].lazy==1)
    {
        tr[i<<1].sum=Len(i<<1),tr[i<<1].lazy=1;
        tr[i<<1|1].sum=Len(i<<1|1),tr[i<<1|1].lazy=1;
    }
    else
    {
        tr[i<<1].sum=0,tr[i<<1].lazy=2;
        tr[i<<1|1].sum=0,tr[i<<1|1].lazy=2;
    }
    tr[i].lazy=0;
}
void updata(int i,int l,int r,int t)
{
    if(l>r || tr[i].l>r || tr[i].r<l)
        return ;
    if(l<=tr[i].l && tr[i].r<=r)
    {
        if(t==0) tr[i].sum=Len(i),tr[i].lazy=1;
        else tr[i].sum=0,tr[i].lazy=2;
        return ;
    }
    if(tr[i].lazy) down(i);
    updata(i<<1,l,r,t);
    updata(i<<1|1,l,r,t);
    tr[i].sum=tr[i<<1].sum+tr[i<<1|1].sum;
}
int query(int i,int l,int r)
{
    if(tr[i].l>r || tr[i].r<l)
        return 0;
    if(l<=tr[i].l && tr[i].r<=r)
        return tr[i].sum;
    if(tr[i].lazy) down(i);
    return query(i<<1,l,r)+query(i<<1|1,l,r);
}
bool check(int x)
{
    build(1,1,n);
    for(int i=1;i<=n;i++)
        updata(1,i,i,a[i]>=x);
    for(int i=1;i<=m;i++)
    {
        int op=s[i].op,l=s[i].l,r=s[i].r;
        int t=query(1,l,r);
        if(op==0)
        {
            updata(1,l,l+t-1,0);
            updata(1,l+t,r,1);
        }
        else
        {
            updata(1,l,r-t,1);
            updata(1,r-t+1,r,0);
        }
    }
    return query(1,q,q)==0;
}
void dich(int l,int r)
{
    if(l>r) return ;
    int mid=(l+r)>>1;
    if(check(mid))
    {
        ans=mid;
        dich(mid+1,r);
    }
    else
        dich(l,mid-1);
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    for(int i=1;i<=m;i++)
        s[i]=node{read(),read(),read()};
    q=read();
    dich(1,n);
    printf("%d\n",ans);
}

其实这道题暴力桶排然后开 O 2 O2 O2也能过。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值