• HihoCoder1677 : 翻转字符串(Splay)(区间翻转)


    描述

    给定一个字符串S,小Hi希望对S进行K次翻转操作。  

    每次翻转小Hi会指定两个整数Li和Ri,表示要将S[Li..Ri]进行翻转。(S下标从0开始,即S[0]是第一个字母)  

    例如对于S="abcdef",翻转S[2..3] 得到S="abdcef";再翻转S[0..5]得到S="fecdba"。

    输入

    第一行包含一个由小写字母组成的字符串S。  

    第二行包含一个整数K。  

    以下K行每行包含两个整数Li和Ri。  

    对于50%的数据,1 ≤ |S| ≤ 1000  

    对于100%的数据,1 ≤ |S| ≤ 100000, 1 ≤ K ≤ 100000, 0 ≤ Li ≤ Ri < |S|

    输出

    输出经过K次翻转后的字符串S

    样例输入

    abcdef  
    2  
    2 3  
    0 5

    样例输出

    fecdba

    比赛的时候以为可以用lazy下压标记,但是后面lazy失去了作用。这道题不能用线段树做的原因不是因为数据范围,而是这道题涉及到了翻转操作,线段树不支持这种操作,所以用splay来维护。

    现在模板初步成型。

    对于初始值: 

    •     如果是插入和查询一起操作,那就按顺序来。
    •     否则,初始时,二分建树。不然一个一个加,会成一条链,然后N^2爆炸。

    对于区间:

    •    可能是有id的,对id在[L,R]之间进行操作,则查找L的前一个点,和R的后一个点,然后操作。(即find函数)
    •    也可能没有id,对当前队伍的从前往后的第[L,R]th之间进行操作,则先查找第L大...。(即findkth函数)
    •    如果存在反转rev操作,也只能用查找第k大来得到区间(上面第二种)。
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<cstring>
    #include<string>
    #include<algorithm>
    using namespace std;
    const int maxn=100010;
    const int inf=1000000000;
    char c[maxn];int n;
    struct SplayTree
    {
        int ch[maxn][2],fa[maxn],rev[maxn],sz[maxn];
        int cnt,root;
        void init()
        {
            cnt=0; root=(1+n+2)>>1; 
        }
        void build(int L,int R,int pre)
        {
            if(L>R) return ;//build不同于线段树,这里有可能大于。 
            if(L==R) {
                fa[L]=pre;sz[L]=1;
                ch[pre][L>=pre]=L;
                return ;
            }int Mid=(L+R)>>1;
            build(L,Mid-1,Mid);build(Mid+1,R,Mid);//这里好像有点乱 
            fa[Mid]=pre; pushup(Mid); 
            ch[pre][Mid>=pre]=Mid;
        }
        void pushup(int x)
        {
            sz[x]=1;
            if(ch[x][0]) sz[x]+=sz[ch[x][0]];
            if(ch[x][1]) sz[x]+=sz[ch[x][1]];
        }
        void pushdown(int x)
        {
            if(!rev[x]) return ;
            swap(ch[x][0],ch[x][1]);
            rev[ch[x][0]]^=1;rev[ch[x][1]]^=1;
            rev[x]=0;
        }
        int find(int x,int rk)//ok
        {
            pushdown(x);//莫忘 
            if(sz[ch[x][0]]==rk-1) return x;
            if(sz[ch[x][0]]>=rk) return (find(ch[x][0],rk));
            return find(ch[x][1],rk-sz[ch[x][0]]-1);
        }
        void rotate(int x,int opt)//ok
        {
            int y=fa[x],z=fa[y];
            pushdown(y);pushdown(x);
            if(z!=0) ch[z][ch[z][1]==y]=x;fa[x]=z;
            ch[y][opt]=ch[x][opt^1];fa[ch[x][opt^1]]=y;
            ch[x][opt^1]=y;fa[y]=x;
            pushup(y);
        }
        void splay(int x,int y)//把x移到y下面 
        {
            pushdown(x); 
            while(fa[x]!=y){
                int f=fa[x],ff=fa[f];
                int c1=(ch[f][1]==x),c2=(ch[ff][1]==f);//记录c1 c2,因为rotate之后儿子关系会改变。 
                if(ff==y) rotate(x,c1);
                else {
                    if(c1^c2) rotate(x,c1),rotate(x,c2);
                    else rotate(f,c2),rotate(x,c1);
                }
            } pushup(x); if(!y) root=x;//提到root 
        }
        void revers(int L,int R)
        {
            int x=find(root,L-1),y=find(root,R+1);
            splay(x,0); splay(y,x);
            rev[ch[y][0]]^=1;
        }
        void print(int x)
        {
            pushdown(x);
            if(ch[x][0]) print(ch[x][0]);
            if(x>=2&&x<=n+1) printf("%c",c[x]); 
            if(ch[x][1]) print(ch[x][1]);
        }
    }Tree;
    int main()
    {
        int i,m,L,R;scanf("%s",c+2); 
        n=strlen(c+2);Tree.init();//莫忘了初始root 
        Tree.build(1,n+2,0); scanf("%d",&m);
        for(i=1;i<=m;i++){
            scanf("%d%d",&L,&R);
            Tree.revers(L+2,R+2);
        } Tree.print(Tree.root);
        return 0;
    }
  • 相关阅读:
    Django的中间件
    电脑端调手机端
    计算两个时间戳之间相差的时间
    去除html标签 php
    Tp5 一次修改多个数据update
    ThinkPhp5.0 引入全局自定义函数global
    layer 使用教程
    phpstrom 汉化
    phpcms 电脑手机合并
    vue 中 v-model
  • 原文地址:https://www.cnblogs.com/hua-dong/p/8169564.html
Copyright © 2020-2023  润新知