首页 > 其他分享 >P2824 [HEOI2016/TJOI2016] 排序

P2824 [HEOI2016/TJOI2016] 排序

时间:2024-04-05 12:44:38浏览次数:20  
标签:P2824 ll mid tag TJOI2016 HEOI2016 ans 排序 ql

简要题意

给定一个长度为 \(n\) 的排列 \(a\),有 \(m\) 次操作:

  • 将 \([l,r]\) 从小到大排序
  • 将 \([l,r]\) 从大到小排序

求 \(m\) 次操作后 \(a_q\) 的值。

\(n,m\leq 10^5\)

思路

首先这种排序的数据结构没有什么想法,根本原因是因为值太多了。但是我们观察到这是一个排列,这对于解这道题目有什么帮助呢?

排列有两个性质:

  1. \(max(a_i)=n\),即值域在 \(n\) 内。
  2. 元素互不相同

如果能利用 \(1\) 性质,去枚举最终的答案并且快速判断就好了。之后考虑如果答案是确定的,那么这么多元素都是没有必要的了。我们可以抽象成 \(\geq ans\) 的元素设为 \(1\) 与 \(<ans\) 的元素设为 \(0\)。

之后排序的操作可以简化成 \(\log_2n\) 级别的了。考虑 \([l,r]\) 中有几个 \(1\),然后把它们都放到最前或最后,用线段树维护即可。但是这仍然是 \(O(nm\log_2n)\) 的,与暴力没有区别。

但是观察到元素互不相同,也就是说明答案唯一。之后发现答案具有单调性。因为如果有 \(\leq ans\) 的元素在 \(q\) 上,那么就可以接着向上找。二分即可。

时间复杂度 \(O(m\log_2^2 n)\)。

代码

#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
const ll MAXN=1e5+5;
struct Query{
    ll op,l,r;
}q[MAXN];
struct node{
    ll val,tag;
}t[MAXN*4];
ll lc(ll u){
    return u<<1;
}
ll rc(ll u){
    return u<<1|1;
}
void push_up(ll u){
    t[u].val=t[lc(u)].val+t[rc(u)].val;
}
ll a[MAXN],b[MAXN];
void build(ll u,ll l,ll r){
    t[u].tag=-1;
    if(l==r){
        t[u].val=b[l];
        return;
    }
    ll mid=(l+r)>>1;
    build(lc(u),l,mid);
    build(rc(u),mid+1,r);
    push_up(u);
}
void push_down(ll u,ll l,ll r){
    if(t[u].tag==-1){
        return;
    }
    ll mid=(l+r)>>1;
    t[lc(u)].tag=t[u].tag;
    t[lc(u)].val=t[u].tag*(mid-l+1);
    t[rc(u)].tag=t[u].tag;
    t[rc(u)].val=t[u].tag*(r-(mid+1)+1);
    t[u].tag=-1;
}
void add(ll u,ll l,ll r,ll ql,ll qr,ll val){
    if(ql<=l&&r<=qr){
        t[u].tag=val;
        t[u].val=(r-l+1)*val;
        return;
    }
    push_down(u,l,r);
    ll mid=(l+r)>>1;
    if(ql<=mid){
        add(lc(u),l,mid,ql,qr,val);
    }
    if(mid+1<=qr){
        add(rc(u),mid+1,r,ql,qr,val);
    }
    push_up(u);
}
ll query(ll u,ll l,ll r,ll ql,ll qr){
    if(ql<=l&&r<=qr){
        return t[u].val;
    }
    push_down(u,l,r);
    ll mid=(l+r)>>1,ans=0;
    if(ql<=mid){
        ans+=query(lc(u),l,mid,ql,qr);
    }
    if(mid+1<=qr){
        ans+=query(rc(u),mid+1,r,ql,qr);
    }
    return ans;
}
ll n,m,Q;
bool check(ll x){
    for(int i=1;i<=n;++i){
        b[i]=a[i]>=x;
    }
    build(1,1,n);
    for(int i=1;i<=m;++i){
        ll op=q[i].op,l=q[i].l,r=q[i].r;
        ll cnt=query(1,1,n,l,r);
        if(cnt==0||cnt==r-l+1){
            continue;
        }
        if(!op){
            add(1,1,n,r-cnt+1,r,1);
            add(1,1,n,l,r-cnt,0);
        }else{
            add(1,1,n,l,l+cnt-1,1);
            add(1,1,n,l+cnt,r,0);
        }
    }
    return query(1,1,n,Q,Q);
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;++i){
        cin>>a[i];
    }
    for(int i=1;i<=m;++i){
        cin>>q[i].op>>q[i].l>>q[i].r;
    }
    cin>>Q;
    ll l=1,r=n,ans=0;
    while(l<=r){
        ll mid=(l+r)>>1;
        if(check(mid)){
            l=mid+1;
            ans=mid;
        }else{
            r=mid-1;
        }
    }
    cout<<ans<<endl;
    return 0;
}

标签:P2824,ll,mid,tag,TJOI2016,HEOI2016,ans,排序,ql
From: https://www.cnblogs.com/tanghg/p/18115646

相关文章

  • P2824 [HEOI2016/TJOI2016] 排序 与 ABC297_g Range Sort Query 题解
    洛谷题目链接:排序abc题目链接:Atcoder或者洛谷两道差不多的题拿出来说说。本题有双\(\log\)做法,也有单\(\log\)做法,都讲讲。双\(\log\)做法对于第一个题而言,询问最终\(pos\)位置上的数为多少,那么这个问题是否具有单调性?这个是很有意思的点,我们考虑只关注某个数\(x\)......
  • P4093 [HEOI2016/TJOI2016] 序列 题解
    题目链接:序列对于LIS问题,很显而易见的有dp方程为:\[dp_i=\max{dp_j}+1\(j<i,a_j\lea_i)\text{dp表示以某个位置结尾的最长LIS}\]本题考虑到对于转移的两位置,如果能从\(j\rightarrowi\),那么在以上条件成立的基础情况下,我们由于可以更改二者中的任意一个值(因为同一......
  • [HEOI2016TJOI2016]排序
    P2824[HEOI2016/TJOI2016]排序直接模拟复杂度爆炸,有观察到它只要求一个数。思维十分清奇。我们先考虑一个序列,如果全是0/1,该怎么做。发现这个问题很好做,修改区间时只需要先查询一的个数,然后将前面/后面全部置1,其他置0。然后我们考虑怎么转化。发现可以二分答案,对于小于mi......
  • P2824 [HEOI2016/TJOI2016] 排序
    针对区间排序,显然能够上值域线段树类似,但这里有个更强的做法。如果能转化成01序列,那么一个区间排序的时候,只需区间询问1的个数+区间修改就可以了。因为是排列,很清晰的二分一个mid,把大于等于它的设为1,小于它的设为0,再跑上面的算法,最后check一下询问位置是否为1即可。单调性?感性......
  • 题解 [HEOI2016/TJOI2016] 排序
    题目链接看到这道题按照套路首先想到二分答案(即二分\(q\)位置上的数,记作\(mid\))。再按照套路将大于\(mid\)的数字设为\(1\),将等于\(mid\)的数设为\(2\),小于\(mid\)的数字设为\(0\)。那么对于区间\([l,r,0]\)操作,应该先讲\(0,1,2\)的数量找出来,然后按照从小到大......
  • P2824 排序(二分答案加线段树)
    传送门很巧妙的一个题直接排序肯定会\(T\)飞我们发现问题只有一个:第\(q\)个位置上的数字不难想到从这里入手,二分答案,考虑第\(q\)个位置上的数字是什么,不妨设他为\(x\)然后把大于等于\(x\)的数变成\(1\),小于\(x\)的数变为\(0\),把他转换为一个\(01\)排序问题:对于区间\([l,r]\)......
  • P2824 排序(二分答案)
    题目简述给出一个$1$到$n$的排列,现在对这个排列序列进行$m$次局部排序,排序分为两种:0lr表示将区间$[l,r]$的数字升序排序1lr表示将区间$[l,r]$的数字降序排序这里是对下标在区间$[l,r]$内的数排序。最后询问第$q$位置上的数字。分析&性质申必题,对......
  • P4093[HEOI2016/TJOI2016]序列
    P4093[HEOI2016/TJOI2016]序列目录P4093[HEOI2016/TJOI2016]序列题目描述输入格式输出格式样例#1样例输入#1样例输出#1提示题目大意思路code题目描述佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他。玩具上有一个数列,数列中某些项的值可能会变化,但同一个......
  • P2824 [HEOI2016/TJOI2016]排序 题解
    题目传送门前言线段树好题!!!!咕咕了挺久的一道题目,很早之前就想写了,今天终于找了个时间A掉了。题意给定一个\(1\)到\(n\)的排列,有\(m\)次操作,分两种类型。1.0lr表示将下标在\([l,r]\)区间中的数升序排序。2.1lr表示将下标在\([l,r]\)区间中的数降序排序。给......
  • LibreOJ L2056 「TJOI / HEOI2016」序列
    https://loj.ac/p/2056CDQ优化DP模板?首先定义对于第\(x\)个数其可以变为的最小值为\(Min_x\),最大值为\(Max_x\),初始为\(M_x\)。因为最多只会变一次数,不难想到......