题解 P5217 【贫穷】
原文链接
看完题目我们都知道是平衡树,而且是要资瓷区间操作的平衡树,并且要能区间翻转,所以我们被迫选择自带三倍大常数的\(Splay\),剩下的都是一些平衡树的基操了。
操作\(1\) :插入,很简单。该怎么插就怎么插
操作\(2\) :删除,直接删除。
操作\(3\) :翻转,将要操作的子树旋转出来,然后直接打标记翻转。
操作\(4\) :开始时将整个序列\(build\)出来,然后用数组将原序列在平衡树中的位置记录下来。平衡树中的一个点被删除后就给那个点打上标记,查询时直接查对应的位置,如果被删除后就输出\(0\),否则先\(dfs\)将路上的标记\(pushdown\)下来,然后再旋转到根输出排名即可。
操作\(5\) :第\(k\)小,直接输出即可。
操作\(6\) :状压压字母,将要操作的子树旋转出来,然后直接输出。
细节
\(1.\)
推荐使用一个这样的函数:
inline int split(int l,int r)
{
int x=rnk(l-1),y=rnk(r+1);
splay(x,0);splay(y,x);
return lc(y);
}
\(rnk\)就是找第\(k\)个位置。
然后你要插入就
int u=split(pos,pos-1);
删除就
int u=split(pos,pos);
找区间就
int u=split(l,r);
\(2.\)
为了不使我们\(split\)时越界,我们就在首尾搞两个字母,然后要把读入后的位置\(++\)。
然后呢?然后就完了。
上代码:
inline int ids(int u) {return u==rc(fc(u));}
inline void connect(int u,int f,int s) {fc(u)=f;tr[f].ch[s]=u;}
inline void pushup(int u)
{
if(!u) return;
tr[u].siz=tr[lc(u)].siz+tr[rc(u)].siz+1;
tr[u].valt=tr[lc(u)].valt|tr[rc(u)].valt|(1<<tr[u].val);
}
inline void update(int u)
{
tr[u].rev^=1;
swap(lc(u),rc(u));
}
inline void pushdown(int u)
{
if(!tr[u].rev) return;
if(lc(u)) update(lc(u));
if(rc(u)) update(rc(u));
tr[u].rev=0;
}
inline void rotate(int u)
{
int f=fc(u),ff=fc(f),s1=ids(u),s2=ids(f);
connect(tr[u].ch[s1^1],f,s1);connect(f,u,s1^1);connect(u,ff,s2);
pushup(f);pushup(u);
}
inline void splay(int u,int to)
{
while(fc(u)!=to)
{
if(fc(fc(u))==to) rotate(u);
else if(ids(u)==ids(fc(u))) rotate(fc(u)),rotate(u);
else rotate(u),rotate(u);
}
}
inline int rnk(int k)
{
int u=root;
while(u)
{
pushdown(u);
if(tr[lc(u)].siz+1==k) return u;
else if(tr[lc(u)].siz>=k) u=lc(u);
else k=k-tr[lc(u)].siz-1,u=rc(u);
}
}
inline int split(int l,int r)
{
int x=rnk(l-1),y=rnk(r+1);
splay(x,0);splay(y,x);
return lc(y);
}
inline void insert(int pos,int val)
{
split(pos,pos-1);
int u=rc(root),v=++utot;lc(u)=v;
tr[v].siz=1;tr[v].fa=u;tr[v].val=val;tr[v].valt=(1<<val);
pushup(u);pushup(fc(u));pushup(fc(fc(u)));
}
inline void erase(int pos)
{
int u=split(pos,pos);delt[u]=1;
lc(fc(u))=0;fc(u)=0;
}
inline void reverse(int l,int r)
{
int u=split(l,r);
update(u);
}
void prepush(int u)
{
if(!u) return;
prepush(fc(u));pushdown(u);
}
inline int getpos(int pos)
{
if(delt[used[pos]]) return 1;
const int &u=used[pos];
prepush(u);splay(u,0);
return tr[lc(u)].siz+1;
}
inline int getcol(int l,int r)
{
int u=split(l,r),res=0;u=tr[u].valt;
while(u) res++,u=u-(u&(-u));
return res;
}
int build(int l,int r,int f)
{
if(l>r) return 0;
int mid=(l+r)>>1,u=++utot;
fc(u)=f;used[mid]=u;tr[u].siz=1;
if(mid!=1&&mid!=n+2) tr[u].val=pre[mid]-'a',tr[u].valt=1<<tr[u].val;
lc(u)=build(l,mid-1,u);
rc(u)=build(mid+1,r,u);
pushup(u);
return u;
}
template<typename T>
inline void read(T &x)
{
char c;int f=1;
while(!isdigit(c=getchar())) (c=='-')&&(f=-1);
x=c^48;
while(isdigit(c=getchar())) x=x*10+(c^48);
x*=f;
}
int main()
{
int x,y,t;
char in[10],opt[10];
read(n);read(t);
scanf("%s",pre+2);
root=build(1,n+2,root);
while(t--)
{
scanf("%s",opt);read(x);x++;
if(opt[0]=='I') scanf("%s",in),insert(x+1,in[0]-'a');
else if(opt[0]=='D') erase(x);
else if(opt[0]=='R') read(y),y++,reverse(x,y);
else if(opt[0]=='P') printf("%d\n",getpos(x)-1);
else if(opt[0]=='T') printf("%c\n",(char)tr[rnk(x)].val+'a');
else read(y),y++,printf("%d\n",getcol(x,y));
}
return 0;
}