• 【BZOJ1269】[AHOI2006] 文本编辑器editor(Splay)


    点此看题面

    大致题意: 让你维护一个字符串,有插入字符串、删除区间、反转区间和输出单个字符操作。

    (Splay)

    这应该是一道比较简单的(Splay)题(虽然因为各种细节我调了很久)。

    我们可以考虑用一个变量(k)来记录光标的位置,然后用(Splay)维护。

    关于用(Splay)维护区间详见这篇博客中关于维护序列的部分:简析平衡树(三)——浅谈Splay

    下面是对各操作实现的简单概括,具体实现见代码。

    (Move)操作

    更新(k)即可。

    (Insert)操作

    首先,将给你的字符串先建成一棵树,记其根为(p)

    然后,将第(k-1)个节点(Splay)到根,第(k+1)个节点(Splay)到根的右儿子,此时根节点的右儿子的左儿子就是第(k)个节点。

    再就可以将(p)作为这个节点的右儿子了(千万注意,要先(PushDown)这个节点再操作,不然会旋转子树)。

    但还要注意维护(Size),一个简单的方法是将(p)直接旋到根即可。

    (Delete)操作

    设删除的区间为([l,r])

    则将第(l-1)个节点(Splay)到根,第(r+1)个节点(Splay)到根的右儿子,然后将根节点的右儿子的左儿子赋值为(0),并(PushUp)根节点的右儿子和根即可。

    (Rotate)操作

    实际上我感觉这个操作应叫(Reverse)操作。。。

    (Delete)操作差不多吧,只不过是把清零改成翻转,且不用(PushUp)罢了。

    (Get)操作

    将第(k-1)个节点(Splay)到根,第(k+1)个节点(Splay)到根的右儿子,此时根节点的右儿子的左儿子就是第(k)个节点。

    返回其权值即可。

    (Prev)操作

    (--k)即可。

    (Next)操作

    (++k)即可。

    关于此题的一些坑点

    1. 这可能是我的(Splay)的问题,一开始在序列的前后需各加两个而不是一个字符作为辅助字符。
    2. 数据有毒。虽然题目中的合法字符集不包括回车符,但数据里的确有,所以读入字符串时应读入(n)个字符。
    3. 依然是回车的问题,如果查询时得到的答案是回车符,则不能再换行。
    4. 不要漏掉任何一个(PushUp)(PushDown),牢记:多写不会(WA)!这点常数不会(TLE)

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define swap(x,y) (x^=y^=x^=y)
    #define hl_AK_NOI true
    using namespace std;
    class FastIO
    {
        private:
            #define FS 100000
            #define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
            #define tn(x) (x<<3)+(x<<1)
            #define D isdigit(c=tc())
            char c,*A,*B,FI[FS];
        public:
            I FastIO() {A=B=FI;}
            Tp I void read(Ty& x) {x=0;W(!D);W(x=tn(x)+(c&15),D);}
            Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
            I void reads(string& x) {x="";W(isspace(c=tc()));W(x+=c,!isspace(c=tc())&&~c);}
            I void readc(char& x) {x=tc();}
    }F;
    class Splay//Splay
    {
        private:
            #define PU(x) (O[x].Sz=O[O[x].S[0]].Sz+O[O[x].S[1]].Sz+1)//上传子树信息,更新Size
            #define Re(x) (swap(O[x].S[0],O[x].S[1]),O[x].R^=1)//翻转一个子树
            #define PD(x) (O[x].R&&(Re(O[x].S[0]),Re(O[x].S[1]),O[x].R=0))//下传翻转标记
            #define Wh(x) (O[O[x].F].S[1]==x)//查询是父节点的哪一个儿子
            #define Co(x,y,d) (O[O[x].F=y].S[d]=x)//连接两个节点
            #define Sp(x,y) (S(GV((x)-1),rt),S(GV((y)+1),O[rt].S[1]),O[O[rt].S[1]].S[0])//抠出一个区间
            static const int SZ=2097152;int rt,tot;struct node {char V;int Sz,R,F,S[2];}O[SZ+5];
            I void Ro(CI x,int& k)//Rotate操作
            {
                RI f=O[x].F,p=O[f].F,d=Wh(x);PD(p),PD(f),PD(x),(f^k?O[p].S[Wh(f)]:k)=x,
                O[x].F=p,Co(O[x].S[d^1],f,d),Co(f,x,d^1),PU(f),PU(x);
            }
            I void S(CI x,int& k) {RI f;W(x^k) f=O[x].F,f^k&&(Ro(Wh(x)^Wh(f)?x:f,k),0),Ro(x,k);PU(x);}//Splay操作
            I int GV(RI rk)//求出排名为k的节点的编号
            {
                RI x=rt;W(hl_AK_NOI)//hl_AK_NOI=true
                {
                    if(PD(x),O[O[x].S[0]].Sz>=rk) x=O[x].S[0];//如果左儿子Size大于等于rk,说明答案在左子树(记得先PushDown)
                    else if(rk-=O[O[x].S[0]].Sz+1) x=O[x].S[1];//如果rk减去左儿子与当前节点的Size和后大于0,就说明答案在右子树
                    else return x;//否则,说明答案在当前节点
                }
            }
        public:
        	I void Init() {rt=Build(0,3,"€€€€");}//初始化(好不容易找到的一个既能显示又不在合法字符集内的字符)
            int Build(CI l,CI r,Con string& s)//建树
            {
                RI x=++tot,t,mid=l+r>>1;O[x].V=s[mid],//二分
                l^mid&&(t=Build(l,mid-1,s),Co(t,x,0)),r^mid&&(t=Build(mid+1,r,s),Co(t,x,1));//处理两个儿子
                return PU(x),x;//返回当前节点编号
            }
            I void Insert(CI x,CI p) {RI k=Sp(x+2,x+2);PD(k),Co(p,k,1),S(p,rt);}//插入
            I void Delete(CI x,CI y) {Sp(x+2,y+2)=0,PU(O[rt].S[1]),PU(rt);}//删除
            I void Rever(CI x,CI y) {RI k=Sp(x+2,y+2);Re(k);}//翻转
            I char Query(CI x) {return O[Sp(x+2,x+2)].V;}//询问
    }S;
    int main()
    {
        RI Qtot,i,x,k=0;Reg string op,s;Reg char c;S.Init(),F.read(Qtot);W(Qtot--)
        {
            F.reads(op);switch(op[0])
            {
                case 'M':F.read(x),k=x;break;//移动光标
                case 'I'://插入
                    for(F.read(x),s="",i=0;i^x;++i) F.readc(c),s+=c;//注意应读入n个字符
                    S.Insert(k,S.Build(0,x-1,s));
                break;
                case 'D':F.read(x),S.Delete(k+1,k+x);break;//删除
                case 'R':F.read(x),S.Rever(k+1,k+x);break;//翻转
                case 'G':putchar(c=S.Query(k+1)),c^'
    '&&putchar('
    ');break;//询问,注意特判回车
                case 'P':--k;break;case 'N':++k;break;//前移和后移
            }
        }return 0;
    }
    
  • 相关阅读:
    svn常用命令
    mysql5.6 sql_mode设置
    centos6.5 mysql5.6主从复制
    linux 挂载windows共享文件夹
    hadoop+hive+hbase+zookeeper安装
    Linux踢出登陆用户的正确姿势
    个人博客项目部署到腾讯云记录(私人记录)
    Python中的单例模式的几种实现方式和优化以及pyc文件解释(转)
    关于window.location.hash的理解及其应用(转)
    Django model反向关联名称的方法(转)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1269.html
Copyright © 2020-2023  润新知