洛谷P5217 贫穷(Splay)
来一发$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; }