P2824 [HEOI2016/TJOI2016] 排序

tanghg發表於2024-04-05

簡要題意

給定一個長度為 \(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)\) 的,與暴力沒有區別。

但是觀察到元素互不相同,也就是說明答案唯一。之後發現答案具有單調性。因為如果有 \(\geq 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;
}

相關文章