[Ynoi2000] Range Sort Prefix Color Number

题目描述

给定整数序列 \(a_1,\dots,a_n\) ,共 \(m\) 次操作;

每次操作给出 \(l,r,x\) ,首先进行修改,然后查询 \(a_1,\dots,a_x\) 中有多少种不同的值。

\(l\le r\) ,则进行的修改是将 \(a_l,\dots,a_r\) 从小到大排序;否则进行的修改是将 \(a_r,\dots,a_l\) 从大到小排序。

强制在线

对于 \(20\%\) 的数据,满足 \(n,m\le 10^3\)

对于另外 \(20\%\) 的数据,满足 \(a_i\le 10\)

对于另外 \(20\%\) 的数据,满足 \(a_i\le 100\)

对于另外 \(20\%\) 的数据,满足 \(n,m\le 10^5\)

对于 \(100\%\) 的数据,满足 \(1\le a_i\le n\)\(1\le l,r,x\le n\)\(1\le n,m\le 10^6\)

\(\rm 8s.\)

题解

发现排序满足颜色段理论,于是考虑直接上 ODT。

发现值域线段树可以合并分裂,于是每个线段维护自己范围内的数,在 ODT 上线段树合并/分裂。(其实这部分可以文艺平衡树)

对于查询,暴力跳连续段肯定时间复杂度爆炸。于是考虑在每个连续段末尾存储该连续段内的答案,放在树状数组上查。散块直接单独查即可。

有些实现细节:实际上由于查询的是前缀,所以可以直接钦定原始数组中第一次出现的数的贡献为 \(1\),其他的则为 \(0\)。可以证明在 stable_sort 的情况下,贡献为 \(1\) 的数永远不会越过贡献为 \(0\) 的。

代码

#include<bits/stdc++.h>

using namespace std;

#define ssiz(x) (signed)x.size()
const int N=1e6+9;

struct Node{
    int lc,rc,cnt,siz;
    void Init(){lc=rc=cnt=siz=0;}
}tr[N<<6];
#define lc(x) tr[x].lc
#define rc(x) tr[x].rc
#define cnt(x) tr[x].cnt
#define siz(x) tr[x].siz

int trs[N<<6],cnt,ttop;
inline int Allc(){return ttop?trs[ttop--]:++cnt;}
inline void Erase(int x){tr[x].Init(),trs[++ttop]=x;}
inline void PushUp(int x){siz(x)=siz(lc(x))+siz(rc(x)),cnt(x)=cnt(lc(x))+cnt(rc(x));}
void Insert(int &x,int L,int R,int pos,int t){
    if(!x) x=Allc();
    if(L==R){
        siz(x)+=t;
        cnt(x)++;
        return ;
    }
    int mid=(L+R)>>1;
    if(pos<=mid) Insert(lc(x),L,mid,pos,t);
    else Insert(rc(x),mid+1,R,pos,t);
    PushUp(x);
}
void Merge(int &x,int &y,int L,int R){
    if(!x||!y) return x|=y,void();
    if(L==R){
        siz(x)+=siz(y);
        cnt(x)+=cnt(y);
        return Erase(y);
    }
    int mid=(L+R)>>1;
    Merge(lc(x),lc(y),L,mid);
    Merge(rc(x),rc(y),mid+1,R);
    PushUp(x),Erase(y);
}
void Split(int &x,int &y,int L,int R,int k,int op){
    if(!x||k==cnt(x)) return ;
    if(!k) return y=x,x=0,void();
    if(!y) y=Allc();
    if(L==R){
        cnt(y)=cnt(x)-k;
        cnt(x)=k;
        if(!cnt(x)) siz(y)=siz(x);
        return ;
    }
    int mid=(L+R)>>1;
    if(!op){
        Split(lc(x),lc(y),L,mid,min(k,cnt(lc(x))),op);
        Split(rc(x),rc(y),mid+1,R,max(k-cnt(lc(x)),0),op);
    }else{
        Split(rc(x),rc(y),mid+1,R,min(k,cnt(rc(x))),op);
        Split(lc(x),lc(y),L,mid,max(k-cnt(rc(x)),0),op);
    }
    PushUp(x),PushUp(y);
}
int Query(int x,int L,int R,int k,int op){
    if(!k||!x) return 0;
    if(k==cnt(x)) return siz(x);
    if(L==R) return siz(x);
    int mid=(L+R)>>1;
    if(!op){
        if(k<=cnt(lc(x))) return Query(lc(x),L,mid,k,op);
        else return siz(lc(x))+Query(rc(x),mid+1,R,k-cnt(lc(x)),op);
    }else{
        if(k<=cnt(rc(x))) return Query(rc(x),mid+1,R,k,op);
        else return siz(rc(x))+Query(lc(x),L,mid,k-cnt(rc(x)),op);
    }
}

int t[N],vis[N],a[N],n,q;
void Add(int x,int k){while(x<=n){t[x]+=k,x+=x&-x;}}
int Ask(int x){int sum=0;while(x){sum+=t[x],x&=x-1;}return sum;}

struct Seg{
    mutable int l,r,op,root;
    Seg(){}
    Seg(int _l,int _r,int _o,int _rt){l=_l,r=_r,op=_o,root=_rt;}
    bool operator <(Seg s)const{return l<s.l;}
};
set<Seg> s;
inline auto Locate(int pos){return pos&&pos<=n?--s.upper_bound(Seg(pos,0,0,0)):s.end();}
void SplitAt(int pos){
    auto it=Locate(pos);
    if(it==s.end()||it->l==pos) return ;
    Seg lt(it->l,pos-1,it->op,it->root),rt(pos,it->r,it->op,0);
    Add(it->r,-siz(it->root));
    s.erase(it);
    Split(lt.root,rt.root,1,n,lt.r-lt.l+1,lt.op);
    Add(lt.r,siz(lt.root));
    Add(rt.r,siz(rt.root));
    s.insert(lt);
    s.insert(rt);
}
void Sort(int l,int r,int op){
    SplitAt(l),SplitAt(r+1);
    auto lt=Locate(l),rt=Locate(r+1);
    Seg tmp(l,r,op,0);
    for(auto it=lt;it!=rt;it++){
        Add(it->r,-siz(it->root));
        Merge(tmp.root,it->root,1,n);
    }
    s.erase(lt,rt);
    Add(tmp.r,siz(tmp.root));
    s.insert(tmp);
}
int QueryAt(int pos){
    auto it=Locate(pos);
    return Ask(it->l-1)+Query(it->root,1,n,pos-it->l+1,it->op);
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    #define endl '\n'

    cin>>n>>q;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++){
        int root=Allc();
        Insert(root,1,n,a[i],!vis[a[i]]);
        s.insert(Seg(i,i,0,root));
        if(!vis[a[i]]) Add(i,1),vis[a[i]]=1;
    }

    int lst=0;
    while(q--){
        int l,r,x;
        cin>>l>>r>>x;
        l^=lst,r^=lst,x^=lst;
        int op=l>r;
        if(op) swap(l,r);
        Sort(l,r,op);
        cout<<(lst=QueryAt(x))<<endl;
    }

    return 0;
}
posted @ 2025-01-20 18:40  JoeyJiang  阅读(8)  评论(0)    收藏  举报