[bzoj3065]带插入区间K小值

题目大意

在线维护一个序列,需要兹瓷插入、修改、求区间K小值。

树套树

开一颗权值线段树。
对于区间[l,r]对应的结点上保存一颗spaly,spaly中的结点权值均在[l,r],按照位置从小到大。
要维护一个结点的位置号。
插入操作,往下走然后插入到spaly中,并打区间加标记来维护位置号。
修改操作,相当于一次删除一次插入。
询问操作,利用线段树特性查找即可。
嘴巴我会,你要我打……还没准备好。

分块

分块。
设c为我们定的块大小。
每一块维护原数组、排序后数组、以及块大小。
然后插入操作时,如果插入的那个块大小刚好为2*c就裂成两个块。
这个打法我是参考swm_sxt的题解
因为是分块所以一切暴力搞就好。
查询自然是最经典的二分,然后转化为判定性问题。块内可以直接二分。

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=70000+10,maxc=600+10,maxd=10000+10;
int next[maxd],size[maxd];
int a[maxd][maxc*2],b[maxd][maxc*2];
int i,j,k,l,t,n,m,c,tot,ans;
bool czy;
char ch;
inline char get(){
    char ch=getchar();
    while (ch!='Q'&&ch!='M'&&ch!='I') ch=getchar();
    return ch;
}
inline void rebuild(int x){
    int i;
    fo(i,1,size[x]) b[x][i]=a[x][i];
    sort(b[x]+1,b[x]+size[x]+1);
}
inline int binary(int x,int y){
    int l=0,r=size[x],mid;
    while (l<r){
        mid=(l+r+1)/2;
        if (b[x][mid]<y) l=mid;else r=mid-1;
    }
    return l;
}
inline int query(int l,int r,int k){
    int ll=1,rr=1,i,j,t;
    while (size[ll]<l){
        l-=size[ll];
        ll=next[ll];
    }
    while (size[rr]<r){
        r-=size[rr];
        rr=next[rr];
    }
    int lll=0,rrr=70000,mid;
    while (lll<rrr){
        mid=(lll+rrr+1)/2;
        t=0;
        if (ll==rr){
            fo(i,l,r) 
                if (a[ll][i]<mid) t++;
        }
        else{
            fo(i,l,size[ll]) 
                if (a[ll][i]<mid) t++;
            fo(i,1,r) 
                if (a[rr][i]<mid) t++;
            j=next[ll];
            while (j!=rr){
                t+=binary(j,mid);
                j=next[j];
            }
        }
        if (t<=k-1) lll=mid;else rrr=mid-1;
    }
    return lll;
}
inline void change(int x,int y){
    int i,j=1,k;
    while (size[j]<x){
        x-=size[j];
        j=next[j];
    }
    fo(i,1,size[j])
        if (b[j][i]==a[j][x]) break;
    b[j][i]=a[j][x]=y;
    k=i;
    while (k>1&&b[j][k]<b[j][k-1]){
        swap(b[j][k],b[j][k-1]);
        k--;
    }
    while (k<size[j]&&b[j][k]>b[j][k+1]){
        swap(b[j][k],b[j][k+1]);
        k++;
    }
}
inline void insert(int x,int y){
    int i,j=1,k;
    while (size[j]<x){
        x-=size[j];
        j=next[j];
    }
    fd(i,size[j],x+1) a[j][i+1]=a[j][i];
    size[j]++;
    a[j][x+1]=y;
    b[j][size[j]]=y;
    if (size[j]==2*c){
        next[++tot]=next[j];
        next[j]=tot;
        fo(i,1,c) a[tot][i]=a[j][i+c];
        size[j]=size[tot]=c;
        rebuild(j);rebuild(tot);
    }
    else{
        k=size[j];
        while (k>1&&b[j][k]<b[j][k-1]){
            swap(b[j][k],b[j][k-1]);
            k--;
        }
    }
}
inline int read(){
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
int main(){
    freopen("3065.in","r",stdin);freopen("3065.out","w",stdout);
    czy=1;
    c=600;
    n=read();
    fo(i,1,n){
        t=read();
        a[(i-1)/c+1][(i-1)%c+1]=t;
        size[(i-1)/c+1]++;
    }
    tot=(n-1)/c+1;
    fo(i,1,tot){
        rebuild(i);
        if (i<tot) next[i]=i+1;
    }
    m=read();
    while (m--){
        ch=get();
        if (ch=='Q'){
            j=read();k=read();l=read();
            if (czy) j^=ans,k^=ans,l^=ans;
            ans=query(j,k,l);
            printf("%d\n",ans);
        }
        else if (ch=='M'){
            j=read();k=read();
            if (czy) j^=ans,k^=ans;
            change(j,k);
        }
        else{
            j=read();k=read();
            if (czy) j^=ans,k^=ans;
            insert(j-1,k);
        }
    }
    fclose(stdin);fclose(stdout);
}

你可能感兴趣的:([bzoj3065]带插入区间K小值)