Description
Phorni 是一个音之妖精,喜欢在你的打字机上跳舞。
一天,阳光映射到刚刚淋浴过小雨的城市上时,Phorni 用魔法分裂出了许多个幻影,从 1 到 n 编号。
她的每一个幻影都站在打出的字符串的一个位置上,多个幻影可以站在同一个位置上。
每一个幻影代表的字符串即为从它站立位置开始的后缀,注意站立位置是从右往左数的。
让我们形式化地描述一下,若第 i 个幻影站在 Pi 上,那么它所代表的字符串就是 S[L-Pi+1…L],其中 L 是字符串 S 的长度。
每一次,她会选一段编号区间 [l..r],而编号在这个区间中的幻影中,字典序最小的一个将跳一支舞,若有多个幻影字典序相同,选编号最小的。
当然由于 Phorni 还会在打字机上跳动,所以有时字符串的前面会加入一个字符。
当然这个打字机是带加密功能的。
字典序的比较:
将两个字符串逐位比较,长度不足的向后补 0 ( 0 小于任何字符) 。直到比出大小或判定相等。
比如 “pho” > “ph” , “pb” > “pab” 。
下标从 1 开始,保证涉及到的所有字符都为小写字母。
Input
输入共 m + 3 行,
第一行为三个整数 n, m, len, type, 分别代表幻影个数,操作次数,初始字符串长度。type = 1 时表示所有的字符都经过了加密。
第二行为初始字符串 S 。
第三行,n 个数 Pi ,意义如题面所示。
接下来 m 行,每行表示一个操作。
1. I c 若 type = 0 表示在字符串前面加入第 c + 1 个小写字母,若 type = 1 则表示加入第 (c xor lastans) + 1 个小写字母,lastans 表示上一次的答案,初始为 0 。
2. C x pos 表示第 x 个幻影跳到了从右向左数 pos 的位置上。
3. Q l r 表示询问[l..r] 。
Output
对于每个询问操作输出一行,表示去跳舞的幻影编号。
用后缀平衡树支持动态O(1)查询后缀间的大小关系,O(logn)插入新后缀,线段树支持查询
后缀平衡树的具体实现:
建一颗含有约262个点的满二叉树,记录其中序遍历标号,用treap维护后缀从小到大排列构成的序列,并维护treap中每个点x对应满二叉树中的点rk[x]作为标号,由于有中序遍历的标号,可以O(1)判断两个点在treap中的先后关系。s[a..n]<s[b..n] 等价于 s[a]<s[b]||s[a]==s[b]&&s[a+1..n]<s[b+1..n] 等价于 rk[a]<rk[b],于是可以支持插入新后缀,新点插入完成后重新暴力计算新点所在子树的标号。由于treap期望重量平衡,可以保证期望O(logn)的插入。由于期望深度较小,treap一般不会超出满二叉树的范围而导致标号失效。
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> char buf[30000000],*ptr=buf-1; void _(int&x){ x=0; int c=*++ptr; while(c<48)c=*++ptr; while(c>47)x=x*10+c-48,c=*++ptr; } void _(char*s){ int c=*++ptr; while(c<33)c=*++ptr; while(c>32)*s++=c,c=*++ptr; *s=0; } typedef long long i64; int n,m,len,tp,la=0; char s[300007],op[8]; int sn,tr[1<<20|111],mx,ps[500007]; i64 rk[300007]; int _min(int a,int b){ i64 z=rk[ps[a]]-rk[ps[b]]; return (z?z<0:a<b)?a:b; } void up(int w){ int l=w<<1,r=l^1; tr[w]=_min(tr[l],tr[r]); } struct node{ node*lc,*rc; int id,rnd; bool operator<(const node&w)const{ return s[id]<s[w.id]||s[id]==s[w.id]&&rk[id-1]<rk[w.id-1]; } void split(node*x,node*&l,node*&r){ if(!this){l=r=0;return;} if(*x<*this){ lc->split(x,l,lc); r=this; }else{ rc->split(x,rc,r); l=this; } } void rebuild(i64 l,i64 r){ if(!this)return; i64 m=rk[id]=(l+r)>>1; lc->rebuild(l,m-1); rc->rebuild(m+1,r); } node*ins(node*w,i64 l,i64 r){ if(!this||rnd<w->rnd){ split(w,w->lc,w->rc); w->rebuild(l,r); return w; } if(*w<*this)lc=lc->ins(w,l,rk[id]-1); else rc=rc->ins(w,rk[id]+1,r); return this; } }ns[300007],*rt; void ins(int w){ ns[w].id=w; ns[w].rnd=rand(); rt=rt->ins(ns+w,0,1ll<<62); } int main(){ fread(buf,1,sizeof(buf),stdin)[buf]=0; rt=ns; rt->rebuild(0,1ll<<62); _(n);_(m);_(len);_(tp); srand(n^m+len^1231+tp); _(s+1); sn=strlen(s+1); std::reverse(s+1,s+sn+1); for(int i=1;i<=sn;++i)ins(i); for(mx=1;mx<=n+4;mx<<=1); for(int i=1;i<=n;++i)_(ps[i]),tr[mx+i]=i; for(int i=mx-1;i;--i)up(i); while(m--){ _(op); if(*op=='I'){ int x; _(x); if(tp)x^=la; s[++sn]='a'+x; ins(sn); }else if(*op=='C'){ int x,y; _(x);_(y); ps[x]=y; for(int w=mx+x>>1;w;w>>=1)up(w); }else{ int l,r; _(l);_(r); int ans=tr[mx+l]; for(l+=mx-1,r+=mx+1;r-l!=1;l>>=1,r>>=1){ if(~l&1)ans=_min(ans,tr[l^1]); if(r&1)ans=_min(ans,tr[r^1]); } printf("%d\n",la=ans); } } return 0; }