题解 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;
}
posted @ 2020-09-02 21:42  试试事实上吗  阅读(122)  评论(0编辑  收藏  举报
Live2D