1. 程式人生 > >[模擬賽] 補番計劃

[模擬賽] 補番計劃

模擬 替換 計劃 CP div main etc 區間 min

Description

小X,作為一只死宅,又攢了好多新番要補。

然而由於他的大多數時間都被用來學OI(推galgame)了,他補番的方案要精打細算。

於是他把所有要補的番排成一個長度為\(n\)序列,利用這個序列優化他的方案。

具體說,序列中的每部番都有一個特征值\(a_i\)

而由於他每天的心情不同,想看番的期望值\(k\)也不同。

每次,他會選擇一個區間\([l,r]\),然後把這個區間內特征值\(a_i\)和他的期望值\(k\)相同的番都補一集。

他想知道這次操作要補多少集番,來預估自己晚上有沒有時間睡覺。

當然,由於某些特殊的影響,小X會放棄某個番劇\(i\),並將其替換,於是他會把\(a_i\)

的值修改為\(x\)

對於小X的每次詢問操作,你都需要輸出他補多少集番。

Input

第一行兩個整數\(n,m\),表示序列長度和操作次數。

第二行\(n\)個整數\(a_1\)-\(a_n\),表示初始序列。

接下來\(m\)行,每行一個整數\(o\),表示操作類型。

如果\(o = 1\),表示這是一個詢問操作,接下來三個整數\(l,r,k\),表示小X補番的區間和期望的特征值\(k\)

如果\(o = 2\),表示這是一個修改操作,接下來兩個整數\(p,x\),表示小X將序列第\(p\)項替換為特征值為\(x\)的番劇。

強制在線,\(l\)\(r\)\(p\)都要加上上一次的答案\(lastans\)

並對\(n\)取模並\(+1\),如果取模後\(l>r\),請自行交換\(l,r\)

Output

對於每個詢問操作,輸出一個整數\(ans_i\),表示小X補番的數量。

Hint

對於\(100\%\)的數據,\(n,m\leq 7\times 10^5\)

所有特征值\(a_i\)均不超過\(MAX_{}INT\)且非負。

時間限制\(3s\),空間限制\(40MB\)

Source

Cmd2001

Solution

乍一看是 \(7e5\) 的強制在線數顏色,但是詢問的是特定值的個數。

所以考慮對於每個特征值維護一棵平衡樹。

對於每個特征值我們用一棵 \(fhq_{}Treap\) 維護有哪些位置有這個特征值。

\(1\) 操作,可以把這棵樹上 \(split\)\([l,r]\) 這段區間,然後求根節點的 \(size\) 就好。

\(2\) 操作,先在這個位置原來的特征值中的 \(Treap\) 中刪掉這個位置,然後將新的特征值的 \(Treap\) 裏插入當前位置即可。

水題水題~
那你還打40分暴力

Code

#include<map>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#define min(A,B) (A)<(B)?(A):(B)
#define max(A,B) (A)>(B)?(A):(B)
#define N 700005+1005
#define inf 0x3f3f3f3f

int n,m;
int a[N];
int cur,cnt;
int del_cur;
int root[N];
int delpool[1005];
int prio[N],val[N];
int sze[N],ch[N][2];
std::map<int,int> mp;

int newnode(){
    if(del_cur) return delpool[del_cur--];
    return ++cur;
}

void read(int &x){
    x=0; char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}

void write(int x){
    if(x>9) write(x/10);
    putchar(x%10+‘0‘);
}

void pushup(int now){
    sze[now]=sze[ch[now][0]]+sze[ch[now][1]]+1;
}

void split(int now,int k,int &x,int &y){
    if(!now) x=y=0;
    else{
        if(val[now]<k){
            x=now;
            split(ch[now][1],k,ch[now][1],y);
        }
        else{
            y=now;
            split(ch[now][0],k,x,ch[now][0]);
        }
        pushup(now);
    }
}

int merge(int x,int y){
    if(!x or !y) return x+y;
    if(prio[x]<prio[y]){
        ch[x][1]=merge(ch[x][1],y);
        pushup(x);
        return x;
    }
    ch[y][0]=merge(x,ch[y][0]);
    pushup(y);
    return y;
}

int new_node(int b){
    int now=newnode();
    sze[now]=1;
    val[now]=b;
    prio[now]=rand();
    ch[now][0]=ch[now][1]=0;
    return now;
}

void insert(int c,int b){
    int x,y;
    split(root[c],b,x,y);
    root[c]=merge(x,merge(new_node(b),y));
}

int query(int e,int b,int c){
    int x,y,z,d;
    split(root[c],e,x,y);
    split(y,b+1,y,z);
    int k=sze[y];
    root[c]=merge(x,merge(y,z));
    return k;
}

void remove(int c,int b){
    int x,y,z;
    split(root[c],b,x,y);
    split(y,b+1,y,z);
    root[c]=merge(x,z);
    delpool[++del_cur]=y;
}

signed main(){
    srand(2002+01+22);
    read(n),read(m);
    for(int x,i=1;i<=n;i++){
        read(x); a[i]=x;
        if(!mp[x]) mp[x]=++cnt;
        insert(mp[x],i);
    }
    int last=0;
    while(m--){
        int o,l,r,k;
        read(o);
        if(o==1){
            read(l),read(r),read(k);
            l=(l+last)%n+1;
            r=(r+last)%n+1;
            if(l>r) l^=r^=l^=r;
            last=query(l,r,mp[k]);
            write(last); putchar(\n);
        }
        else{
            read(l),read(r);
            l=(l+last)%n+1;
            if(!mp[r]) mp[r]=++cnt;
            remove(mp[a[l]],l);
            a[l]=r;
            insert(mp[r],l);
        }
    }
    return 0;
}

[模擬賽] 補番計劃