洛谷P5217 贫穷

来一发$Splay$~~~

首先一个个操作分析:

1. I操作,$Splay$插入。
2. D操作,$Splay$删除。
3. R操作,可以参考洛谷P3391,把$[x,y]$从树中分裂出来,打标记
4. P操作,在建树时把$1-n$号字符所在的结点编号记下来,判断该点是否被删除(在删除时加个记号),如果没有,先把结点上方的标记全部下推,然后将结点旋到根,输出排名
5. T操作,将排名第$x$的结点旋到根
6. Q操作,考虑字母只有26个,用$1,2,...2^{25}$状压维护一下,询问时同样把$[x,y]$从树中分裂出来,输出

#include<cstdio>
#include<cctype>
#define il inline
const int N=200050;
char rB[1<<21],*S,*T,wB[1<<21];
int wp=-1;
il char gc(){return S==T&&(T=(S=rB)+fread(rB,1,1<<21,stdin),S==T)?EOF:*S++;}
il void flush(){fwrite(wB,1,wp+1,stdout);wp=-1;}
il void pc(char c){if(wp+1==(1<<21))flush();wB[++wp]=c;}
il char rdu(){
    char c=gc();
    while(!isupper(c))c=gc();
    return c;
}
il char rdc(){
    char c=gc();
    while(!islower(c))c=gc();
    return c;
}
il int rdi(){
    char c=gc();
    while(!isdigit(c))c=gc();
    int x=c&15;
    for(c=gc();isdigit(c);c=gc())x=(x<<3)+(x<<1)+(c&15);
    return x;
}
//快读
short buf[15];
il void wt(int x){
    short l=-1;
    while(x>9){
        buf[++l]=x%10;
        x/=10;
    }
    pc(x|48);
    while(l>=0)pc(buf[l--]|48);
    pc('\n');
}
//快写
char v[N],st[N>>1];
int pos[N>>1],s[N],fa[N],ch[N][2],f[N],sz=0;
bool lazy[N];
il void rds(){  //这个也是快读
    char c=gc();
    while(!islower(c))c=gc();
    st[1]=c;
    for(int i=2,c=gc();islower(c);i++,c=gc())st[i]=c;
}
il void Swap(int &a,int &b){int t=a;a=b;b=t;}
il short count(int x){  //数x二进制位中1的个数,即字符种类数
    short s=0;
    for(;x;x^=x&-x)s++;
    return s;
}
//模板来了
struct Splay{
    int rt;
    il int newnode(char c){
        v[++sz]=c;s[sz]=1;fa[sz]=ch[sz][0]=ch[sz][1]=0;f[sz]=1<<c-'a';lazy[sz]=0;
        return sz;
    }
    il void maintain(int o){  //维护结点信息
        s[o]=1+s[ch[o][0]]+s[ch[o][1]];  //该结点子树大小
        f[o]=(1<<v[o]-'a')|f[ch[o][0]]|f[ch[o][1]];  //该结点子树所含字符种类
    }
    il void pushdown(int o){  //下推懒标记
        if(lazy[o]){
            Swap(ch[o][0],ch[o][1]);
            lazy[ch[o][0]]^=1;lazy[ch[o][1]]^=1;
            lazy[o]=0;
        }
    }
    il short cmp(int o,char c){  //比较
        if(v[o]==c)return -1;
        else return c>v[o];
    }
    il bool dir(int o){return ch[fa[o]][1]==o;}  //o是它父亲的左/右儿子(0表示左,1表示右,以下均同)
    il void rotate(int o){  //旋转,将o旋到它父亲的位置
        int f=fa[o];
        short d=dir(o);
        fa[o]=fa[f];
        if(f==rt)rt=o;
        else ch[fa[f]][dir(f)]=o;
        if(ch[f][d]=ch[o][d^1])fa[ch[f][d]]=f;
        fa[ch[o][d^1]=f]=o;
        maintain(f);maintain(o);
    }
    il void splay(int o){  //Splay最核心的内容,将o旋转到根
        for(;fa[o];rotate(o))if(fa[fa[o]])rotate(dir(fa[o])==dir(o)?fa[o]:o);
    }
    void build(int &o,int L,int R){  //开始建一棵完美Splay
        int M=L+R>>1;
        o=newnode(st[M]);pos[M]=o;  //P操作用的编号
        if(L<M){
            build(ch[o][0],L,M-1);
            fa[ch[o][0]]=o;
        }
        if(M<R){
            build(ch[o][1],M+1,R);
            fa[ch[o][1]]=o;
        }
        maintain(o);
    }
    il void find(int k){  //找到排名为k的结点并将其旋到根
        int o=rt,t;
        for(;;){
            pushdown(o);  //每访问一个结点都要记得下推标记
            t=s[ch[o][0]];
            if(t+1==k)break;
            else if(k<=t)o=ch[o][0];
            else{
                k-=t+1;
                o=ch[o][1];
            }
        }
        splay(o);
    }
    il int split(bool d){  //将树根的左/右儿子分裂出来
        pushdown(rt);
        int t=ch[rt][d];
        ch[rt][d]=0;fa[t]=0;
        maintain(rt);
        return t;
    }
    il void merge(int t,bool d){  //合并到树的最左/右侧
        if(!rt)rt=t;
        else{
            find(d?s[rt]:1);
            fa[ch[rt][d]=t]=rt;
            maintain(rt);
        }
    }
    il void ins(int k,char c){  //I操作,本来可以直接用find,因为写得早忘记了
        if(!rt)rt=newnode(c);
        else{
            int o=rt,t;
            for(;;){
                pushdown(o);
                t=s[ch[o][0]];
                if(t+1==k)break;
                else if(k<=t)if(ch[o][0])o=ch[o][0];
                else{  //这里注意当k=0,即插在最前面时,是找不到第0结点的,特判一下
                    fa[ch[o][0]=newnode(c)]=o;
                    maintain(o);
                    splay(o);  //做完操作都splay一下可以保证树的随机性
                    return;
                }else{
                    k-=t+1;
                    o=ch[o][1];
                }
            }
            splay(o);
            t=split(1);
            fa[ch[o][1]=newnode(c)]=o;
            fa[ch[ch[o][1]][1]=t]=ch[o][1];
            maintain(ch[o][1]);
            maintain(o);
//先将o旋到根,再分裂右子树,将新结点变成o的右儿子,原来的右子树变成新结点的右儿子,就成功将新结点插入到o的右侧
        }
    }
    il void del(int k){  //D操作,同样可以用find简化
        int o=rt,t;
        for(;;){
            pushdown(o);
            t=s[ch[o][0]];
            if(t+1==k)break;
            else if(k<=t)o=ch[o][0];
            else{
                k-=t+1;
                o=ch[o][1];
            }
        }
        splay(o);
        t=split(1);
        v[o]='#';  //以'#'作为删除标记
        fa[rt=ch[o][0]]=0;
        merge(t,1);
//先将o旋转到根,再合并它的左右子树
    }
    void allpush(int o){  //对o的所有祖先做下推
        if(o!=rt)allpush(fa[o]);
        pushdown(o);
    }
}tree;
int main(){
    int n=rdi(),m=rdi(),x,y,t1,t2;
    char opt,c;
    rds();
    tree.build(tree.rt,1,n);
    while(m--){
        opt=rdu();x=rdi();
        if(opt=='I'){
            c=rdc();
            tree.ins(x,c);
        }else if(opt=='D')tree.del(x);
        else if(opt=='R'){
            if((y=rdi())<x)Swap(x,y);
            tree.find(y);t2=tree.split(1);
            tree.find(x);t1=tree.split(0);
//将区间[x,y]分裂出来,先找x的话y的排名会改变,不如先找y方便
            lazy[tree.rt]^=1;
            tree.merge(t1,0);
            tree.merge(t2,1);
        }else if(opt=='P')if(v[pos[x]]=='#'){pc('0');pc('\n');}
        else{
            tree.allpush(pos[x]);
            tree.splay(pos[x]);
            wt(s[ch[tree.rt][0]]+1);
        }else if(opt=='T'){
            tree.find(x);
            pc(v[tree.rt]);pc('\n');
        }else if(opt=='Q'){
            if((y=rdi())<x)Swap(x,y);
            tree.find(y);t2=tree.split(1);
            tree.find(x);t1=tree.split(0);
            wt(count(f[tree.rt]));
            tree.merge(t1,0);
            tree.merge(t2,1);
        }
    }
    flush();
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/sunshine-chen/p/11258802.html
今日推荐