zoukankan      html  css  js  c++  java
  • splay总结

    splay树对于区间操作比线段树更容易操作,编写代码也更容易,调试起来也很方便。以下我大概说说splay的几个操作以及我对几个题目的分析。

    首先是资料,请点击 here 下载(如果不行的话,麻烦跟我说一下,谢谢)

     

    以下我们先定义几个数组和变量:

    // lx表示x的左儿子,rx表示x的右儿子,px表示x的父节点,root表示根节点,其他的类似
    #define lx ch[x][0]
    #define rx ch[x][1]
    #define px pre[x]
    
    #define ly ch[y][0]
    #define ry ch[y][1]
    #define py pre[y]
    
    #define lz ch[z][0]
    #define rz ch[z][1]
    #define pz pre[z]
    
    #define rt ch[root][1]
    #define lrt ch[rt][0]
    // ch数组表示x的左右儿子,pre表示x的父节点,sz表示大小,rev表示区间翻转标记,str表示插入的字符串
    int ch[X][2],pre[X],sz[X],rev[X]; 
    char str[X];
    

      

    首先,对于初始化建树的问题。我们首先需要建立两个或者以上的额外节点,建立的过程如下图:

      (节点1,2都是一个额外的节点(跟题目没有半毛钱关系。。。))

    比如我们需要用splay维护字符串abcdefgh:

    1.建树,如上

    2.在 根的右儿子的左儿子 中插入

    3.为了使得树的深度不太深,我们需要递归把字符串加在 根的右儿子的左儿子 上。建完树之后应该如下:(可以假设两个虚拟节点1,2的字符为未出现在题目中的字符)

    该代码如下:

        // 初始化建树的问题
      inline void new_node(int &x,int y,int c){ // 注意x需要在这修改,修改之后的值需要传回上一个函数
            x = ++tot;
            ha[x] = val[x] = c;
            px = y;
            lx = rx = 0;
        }
     
        inline void build(int &x,int y,int l,int r){ // 对于区间[l,r]的插入
            if(l>r) return;
            int mid = (l+r)>>1;
            new_node(x,y,str[mid]-'a'+1);  // 其他的题目不一定是str数组了
            build(lx,x,l,mid-1);
            build(rx,x,mid+1,r);
            update(x);
        }
      
     
      inline void init(){
        // 初始化,如果只用一次的话,我们定义的是全局变量,所以不用置为0,如果需要的话,我们也可以直接把根的所有有关数组置为0就行了,无需把全部清零
            //memset(ch,0,sizeof(ch));
            //memset(sz,0,sizeof(sz));
            //memset(pre,0,sizeof(pre));
     
            root = tot = 0;
            new_node(root,0,0);
            new_node(rt,root,0);
     
            update(rt);  // 需要更新一下新插入的节点,具体看题目而定
            update(root);
     
            gets(str);
            int n = strlen(str);
            build(lrt,rt,0,n-1);
            update(rt);
            update(root);
        }
    

      

     update函数一般的写法如下:

        inline void update(int x){
            sz[x] = sz[lx]+sz[rx]+1;
            // 如果需要维护一下sum,lsum,rsum或者hash的话,在这里添加相应代码
        }
    

    push_down函数看具体的题目而定,主要是及时把lazy标记下沉

    然后是splay和旋转操作,由于给出的资料或者其它的一些资料已经很详细了,这里不细说。splay(x,goal)操作就是把x旋转到x的父节点为goal的操作,他是自底向上的操作。在操作过程中,zag,zig其实就相当于普通的左旋、右旋。以下为代码:

    	inline int sgn(int x){ // 判断x是y的左或者右儿子,0左,1右
            return ch[px][1]==x;
        }
        inline void setc(int y,int d,int x){ // 把x置为y的儿子
            ch[y][d] = x;
            px = y;
        }
    
        inline void rot(int x,int d){ // 旋转操作,自己动手画画就好了
            int y = px;
            int z = py;
            //push_down(y);
            //push_down(x);
            setc(y,!d,ch[x][d]);
            if(z)   setc(z,sgn(y),x);
            px = z;
            setc(x,d,y);
            update(y);
        }
    
        inline void splay(int x,int goal=0){ // splay的关键操作
            if(!x)
                return;
            //push_down(x);
            while(px!=goal){
                int y = px;
                int z = py;
                if(z==goal){
                    rot(x,!sgn(x));
                    break;
                }
                if(lz==y){
                    ly==x?rot(y,1):rot(x,0);
                    rot(x,1);
                }
                else{
                    ry==x?rot(y,0):rot(x,1);
                    rot(x,0);
                }
            }
            update(x);
            if(goal==0)
                root = x;
        }
    

      

      

    下面我们主要说说splay具体能够做什么

    首先,我们需要知道第k小、得到以x为根的最小值元素,直接贴代码了。。

        inline int get_Kth(int x,int k){
            //push_down(x);   // 是否需要把标记下沉
            int tmp = sz[lx]+1;
            if(tmp==k)
                return x;
            if(k<tmp)
                return get_Kth(lx,k);
            return get_Kth(rx,k-tmp);
        }
    
        inline int get_min(int x){
            //push_down(x);
            while(lx){
                x = lx;
                //push_down(x);
            }
            return x;
        }
    

      

    为了方便描述,rt表示根的右儿子(right_root),  lrt表示根的右儿子的左儿子(left_right_root)

    1.插入操作:在位置k上插入一个元素。

      我们首先需要得到第k+1小的元素下标x(这里的小指的是树中按照sz大小来决定),然后把x旋转至根,旋转完了之后,我们需要得到x的后继y,把y旋转至根的右儿子。这时,我们发现根的右儿子的左子树为空。然后我们在lrt中新建一个节点就行了。

      为什么找的不是第k小的元素呢?还记得我们为了代码更加简单,构造的两个虚拟节点吗?刚好实在插入的所有元素的两端,并且插入的时候实际上是该元素的后面,所以我们找的是第k+1小的元素。

      比如我们在@abcdefgh@的树中,在第4位后面插入新节点q,我们插入的位置是d,e的中间,即第5位和5的后继

         inline void Insert(){ // 插入操作
            int x,k;
            char s[2];
            scanf("%d%s",&k,s);
    
            x = get_Kth(root,k+1); // 为什么不是k?
            splay(x);
            int y = get_Min(rt); // 得到x的后继
            splay(y,root);
    
            new_node(lrt,rt,s[0]-'a'+1); // 新建一个节点
    
            update(lrt); //及时更新插入之后的信息!
            update(rt);
            update(root);
        }
    

      

    2.插入操作二:成段插入。

      假设我们需要插入的位置为pos,然后插入n个元素,那么插入的操作的过程是:首先寻找pos+1的元素x,然后找到他的后继,再把n个元素通过build递归建在lrt上。

        inline void Insert(){
            int pos,n;
            scanf("%d%d",&pos,&n);
            rep(i,k)
                cin>>str[i];
    
            pos ++;
    
            int x = get_Kth(root,pos);
            splay(x);
            int y = get_min(rt);
            splay(y,root);
    
            build(lrt,rt,0,n-1);
            //update(lrt); 可以不需要,在build函数中已经更新
            update(rt);
            update(root);
        }
    

      

    3.删除操作:删除一个区间(一个节点其实也是小区间)

       假设我们需要删除区间[a,b],我们需要找到a的前驱以及b的后继,使得以lrt为根子树恰好为区间[a,b]。首先,我们需要找到第a小元素x,然后把x splay到根,然后再找到b的后继,即找到第b+2小的元素y把y splay到根的右儿子,这时,我们发现区间[a,b]的元素实际上就是以lrt为根的子树的所有元素

      为什么这个时候是a而不是a+1呢?因为第a小才是实际上的a的前驱,而我们执行完上述的操作之后,lrt才是真正的区间[a,b]。这个时候根为区间[a,b]的前一个元素,b+2为后一个元素。

      然后我们把lrt的信息以及rt的信息修改一下就行了,最后需要update(rt),update(root);及时更新一下rt和root

    以下为代码:

        inline void Delete(){ // 删除区间 [a,b]
            int a,b;
            scanf("%d%d",&a,&b);
            int x = get_Kth(root,a);
            int y = get_Kth(root,b+2);
            splay(x);
            splay(y,root);
            //del(lrt); 建立内存池,人工回收节点编号(在删除操作太频繁的时候,可能节点的编号使得数组太大,内存不足)
            pre[lrt] = 0;
            lrt = 0;
            update(rt);
            update(root);
        }
    

      

     4.翻转操作:把区间[a,b]翻转

      我们用一个rev[]数组进行类似于线段树的lazy标记。

      同样我们先把 第a小 旋转到到根,第b+2小 旋转到rt,区间[a,b]即为lrt。这时,我们对于节点lrt的rev标记置反就好了。注意这个时候向不向上更新根据具体的题目而定,一般是不需要的。代码:

        inline void Rotate(){
            int a,b;
            scanf("%d%d",&a,&b);
            int x = get_Kth(root,a);
            splay(x);
            int y = get_Kth(root,b+2);
            splay(y,root);
            rev[lrt] ^= 1;
        }
    

    注意:由于我们有了翻转标记,所以在标记下沉的时候,如果有标记的话,需要把左右子树交换一下。同时,如果有其他的数据维护,考虑一下需不需要交换。。。具体的看 S级别的NOI2005维修数列 的splay操作,做完之后,基本上的区间操作都会做了。

     5.区间赋值:把区间[a,b]的所有元素置为c

      其实我们发现这个跟上面翻转操作基本一样。所以我直接上代码了。。。

        inline void Make_same(){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
    
            int x = get_Kth(root,a);
            int y = get_Kth(root,b+2);
            splay(x);
            splay(y,root);
    
            update_same(lrt,c);
            update(rt);
            update(root);
        }
    

    注意:有标记,考虑下沉时候的操作(需要维护sum,lmax,rmax,val等等...)。  

    6.区间求和:询问区间[a,b]的和

      同样把区间[a,b]定在lrt上面,然后直接调用sum[lrt]就行了。那么如何维护sum[x]呢?其实很简单,在向上update的时候就及时更新好了。

        inline void update(int x){
            sz[x] = sz[lx]+sz[rx]+1;
            sum[x] = sum[lx]+sum[rx]+val[x]; // 多了这个
        }
    
        inline void Get_sum(){
            int a,b;
            scanf("%d%d",&a,&b);
            int x = get_Kth(root,a);
            int y = get_Kth(root,b+2);
            splay(x);
            splay(y,root);
            printf("%d\n",sum[lrt]);
        }
    

      

     7.区间求最值:询问区间[a,b]的最值

      那这个是不是跟sum的操作基本一样呢?代码略了。

    8.求和的最大子序列:询问区间[a,b]的最大子段和

      想法是:对于每个节点,用数组mmax[]来表示以该节点为根的区间的最大子段和。所以我们可以把区间[a,b]定格在lrt,然后直接输出mmax[lrt]。如何维护???

      我们可以在update的时候更新!我们维护一个最大前驱和lmax,一个最大后继和rmax,然后再维护一个mmax,然后我们像以下代码这样维护就好了。

        inline void update(int x){
            sz[x] = sz[lx]+sz[rx]+1;
            //sum[x] = sum[lx]+sum[rx]+val[x];
            lmax[x] = max( lmax[lx] , sum[lx]+val[x]+max(0,lmax[rx]) );
            rmax[x] = max( rmax[rx] , sum[rx]+val[x]+max(0,rmax[lx]) );
            mmax[x] = max( mmax[lx] , mmax[rx] );
            mmax[x] = max( mmax[x] , max(0,lmax[rx])+val[x]+max(0,rmax[lx]) );
        }
        
        inline void Get_max(){
            int a,b;
            int x = get_Kth(root,a);
            int y = get_Kth(root,b+2);
            splay(x);
            splay(y,root);
            printf("%d\n",mmax[lrt]);
        }
    

      可能你会想为什么是这么更新的?这样对的吗?想想,我们的splay是中序遍历的,然后画个图出来看看吧。

    9.单点修改操作

       这个很简单吧,比如我们需要把第k个元素做修改,首先我们找到第k+1,然后旋转至根,直接把根的值修改,update(root)就好了。代码略。。。

     至于完整的模板,其实以那题NOI2005维修数列作为模板就好了。

    貌似其他的操作暂时我也没做到。就这些吧。。。以下是实战区。。。(点击有链接)

     

    BZOJ 1588: [HNOI2002]营业额统计


    这题是我自己的第一道splay题,只是用来测试一下模板的。。。
    分析:
    这题就是求前驱和后继。旋转跟SBT、AVL、BST、treap一样,并且不需要维护树的平衡,默认为90%的询问发生在10%的数据上面,所以我们在插入的时候就把插入的的数据经过splay操作旋转该节点至根部。这题在询问x的前驱的时候,我们可以先得到x,然后再把x经过splay操作旋转至根部,再找到根的前驱,后继同理。其他的操作以后再总结一下。。

    /*
    
    这题是我自己的第一道splay题,只是用来测试一下模板的。。。
    
    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1588
    
    分析:
        这题就是求前驱和后继。旋转跟SBT、AVL、BST、treap一样,并且不需要维护树的平衡,
        默认为90%的询问发生在10%的数据上面,所以我们在插入的时候就把插入的的数据经过
        splay操作旋转该节点至根部。这题在询问x的前驱的时候,我们可以先得到x,然后再把x
        经过splay操作旋转至根部,再找到根的前驱,后继同理。其他的操作以后再总结一下。。
    
    */
    #include <set>
    #include <map>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <string>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    typedef unsigned long long ull;
    
    #define debug puts("here")
    #define rep(i,n) for(int i=0;i<n;i++)
    #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++)
    #define pb push_back
    
    #define INF 1e9
    
    namespace Splay{
    
    #define X 1111111
    
    #define px pre[x]
    #define py pre[y]
    
    #define lx ch[x][0]
    #define ly ch[y][0]
    #define lz ch[z][0]
    
    #define rx ch[x][1]
    #define ry ch[y][1]
    
        int root,tot;
        int ch[X][2],pre[X],val[X];
    
        inline void init(){ // 初始化
            root = tot = 0;
            memset(ch,0,sizeof(ch));
            memset(pre,0,sizeof(pre));
            memset(val,0,sizeof(val));
        }
    
        inline void dfs(int x){ // debug使用
            if(x){
                dfs(lx);
                printf("self = %d , left = %d , right = %d , father = %d\n",x,lx,rx,px);
                dfs(rx);
            }
        }
    
        inline void new_node(int &x,int father,int v){ // 构造新节点
            x = ++tot;
            pre[x] = father;
            val[x] = v;
            ch[x][0] = ch[x][1] = 0;
        }
    
        inline void setc(int y,int d,int x){ // 旋转过程的子树的链接
            ch[y][d] = x;
            pre[x] = y;
        }
    
        inline int sgn(int x){ // 0表示在左,1表示在右
            return ch[px][1]==x;
        }
    
        inline void _rot(int x,int d){
            int y = px;
            int z = py;
    
            setc(y,!d,ch[x][d]);//类似SBT,要把其中一个分支先给父节点
    
            if(z)   //如果父节点不是根结点,则要和父节点的父节点连接起来
                setc(z,sgn(y),x);
            pre[x] = z;
    
            setc(x,d,y);
        }
    
        inline void rot(int x){_rot(x,!sgn(x));}
        inline void zag(int x){_rot(x,0);} // 左旋
        inline void zig(int x){_rot(x,1);} // 右旋
    
        // Splay调整,将根为r的子树调整为goal
        inline int splay(int x,int goal=0){
            while(px!=goal){
                int y = px;
                int z = py;
                if(z==goal){
                    rot(x);
                    break;
                }
                if(lz==y){
                    if(ly==x)
                        zig(y),zig(x);
                    else
                        zag(x),zig(x);
                }
                else{
                    if(ry==x)
                        zag(y),zag(x);
                    else
                        zig(x),zag(x);
                }
            }
            if(goal==0)
                root = x;
            return x;
        }
    
        inline int insert(int v){ // 插入
            int x = root;
            while(ch[x][ val[x]<v ]){
                if(val[x]==v){
                    splay(x); // 已存在,这题可以忽略掉,但是需要旋转该节点作为根
                    return 0;
                }
                x = ch[x][ val[x]<v ];
            }
            new_node(ch[x][ val[x]<v ],x,v);
            splay(ch[x][ val[x]<v ]); // 新插入节点splay至根部
            return 1;
        }
    
        inline int get_pre(int x){ // 得到前驱
            int tmp = x;
            x = lx;
            if(x==0)
                return INF;
            while(rx)
                x = rx;
            return val[tmp]-val[x];
        }
    
        inline int get_next(int x){ // 得到后继
            int tmp = x;
            x = rx;
            if(x==0)
                return INF;
            while(lx)
                x = lx;
            return val[x]-val[tmp];
        }
    
    #undef X
    
    #undef px
    #undef py
    
    #undef lx
    #undef ly
    #undef lz
    
    #undef rx
    #undef ry
    
    }using namespace Splay;
    
    int main(){
    
    #ifndef ONLINE_JUDGE
    	freopen("sum.in","r",stdin);
    #endif
    
        int ans = 0,n,x;
        cin >> n;
    
        init();
        rep(i,n){
            if(scanf("%d",&x)==EOF)
                x = 0;
            if(i==0){
                ans += x;
                new_node(root,0,x);
                continue;
            }
            if(insert(x)==0)
                continue;
            // 前面插入的时候x已经splay至根部,所以可以直接求值
            ans += min(get_pre(root),get_next(root));
        }
        cout<<ans<<endl;
    
    	return 0;
    }
    

      

     SGU 187. Twist and whirl - want to cheat

     对于数列,求m次区间翻转之后的数列(比较简单。。。)

    /*
    
    题目:对于数列,求m次区间翻转之后的数列
    
    分析:splay简单操作
    
    */
    #include <set>
    #include <map>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <string>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    typedef unsigned long long ull;
    
    #define debug puts("here")
    #define rep(i,n) for(int i=0;i<n;i++)
    #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++)
    #define pb push_back
    #define RD(n) scanf("%d",&n)
    
    namespace Splay{
    
    #define lx ch[x][0]
    #define rx ch[x][1]
    #define px pre[x]
    
    #define ly ch[y][0]
    #define ry ch[y][1]
    #define py pre[y]
    
    #define lz ch[z][0]
    
    #define rt ch[root][1]
    #define lrt ch[rt][0]
    
        const int MAXN = 130015;
    
        int ch[MAXN][2],pre[MAXN],val[MAXN],sz[MAXN];
        bool rev[MAXN];
        int root,tot;
        int ary[MAXN];
        bool ok;
    
        inline void update(int x){ // ok
            sz[x] = sz[lx]+sz[rx]+1;
        }
    
        inline void new_node(int &x,int y,int v){ // ok
            x = ++tot;
            px = y;
            rev[x] = 0;
            lx = rx = 0;
            val[x] = v;
        }
    
        inline void build(int &x,int y,int l,int r){ // ok
            if(l>r) return;
            int mid = (l+r)>>1;
            new_node(x,y,ary[mid]);
            build(lx,x,l,mid-1);
            build(rx,x,mid+1,r);
            update(x);
        }
    
        inline void push_down(int x){   // ok
            if(rev[x]==0)   return;
            swap(lx,rx);
            rev[x] = 0;
            rev[lx] ^= 1;
            rev[rx] ^= 1;
        }
    
        inline int sgn(int x){ // ok
            return ch[px][1]==x;
        }
    
        inline void setc(int y,int d,int x){ // ok
            ch[y][d] = x;
            px = y;
        }
    
        inline void rot(int x,int d){ // ok
            int y = px;
            int z = py;
            push_down(y);
            push_down(x);
            setc(y,!d,ch[x][d]);
            if(z)   setc(z,sgn(y),x);
            pre[x] = z;
            setc(x,d,y);
            update(y);
        }
    
        inline void splay(int x,int goal=0){ // ok
            push_down(x);
            while(px!=goal){
                int y = px;
                int z = py;
                if(z==goal){
                    rot(x,!sgn(x));
                    break;
                }
                if(lz==y){
                    if(ly==x)
                        rot(y,1),rot(x,1);
                    else
                        rot(x,0),rot(x,1);
                }
                else{
                    if(ry==x)
                        rot(y,0),rot(x,0);
                    else
                        rot(x,1),rot(x,0);
                }
            }
            update(x);
            if(goal==0)
                root = x;
        }
    
        inline int get_Kth(int x,int k){ // ok
            push_down(x);
            int tmp = sz[lx]+1;
            if(tmp==k)  return x;
            if(k<tmp)   return get_Kth(lx,k);
            else        return get_Kth(rx,k-tmp);
        }
    
        inline void dfs(int x){ // 调试用的,忽略。。
            if(x==0)    return;
            push_down(x);
            dfs(lx);
            if(val[x]){
                ok?printf(" "):ok = 1;
                printf("%d",val[x]);
            }
            dfs(rx);
        }
    
        inline void solve(){
            ok = 0;
            int n,m;
            scanf("%d%d",&n,&m);
            int x,y;
    
            root = tot = 0;
            ch[0][0] = ch[0][1] = pre[0] = rev[0] = 0;
    
            ary[0] = 0;
            for(int i=1;i<=n;i++)
                ary[i] = i;
            new_node(root,0,0);
            new_node(rt,root,0);
    
            build(lrt,rt,1,n);
    
            while(m--){
                scanf("%d%d",&x,&y);
                x = get_Kth(root,x);
                splay(x);
                y = get_Kth(root,y+2);
                splay(y,root);
                rev[lrt] ^= 1;
            }
            dfs(root);
            puts("");
        }
    
    }using namespace Splay;
    
    int main(){
    
    #ifndef ONLINE_JUDGE
    	freopen("sum.in","r",stdin);
    #endif
    
        solve();
    
    	return 0;
    }
    

      

     BZOJ 1014 [JSOI2008]火星人prefix

    分析:
    动态计算LCP。
     
    静态的话,可以用后缀数组来计算,但是对于动态的话就无能为力了。。。如果用询问来计算LCP的话,会TLE的,所以我们可以想到用二分+hash来计算。但是计算hash的话,由于是动态并且是区间统计问题,所以我们可以用splay节点表示字符,并且记录以该节点为根的子树所在的区间的字符串的hash值,判断是否相等就行了。。。
     
    hash函数的选取,用RKhash:
    hash[a,b] = ch[a]*1+ch[a+1]*27+...+ch[b]*27^(b-a)
     
    需要注意的是:
    1.对于首字符都不同的话,直接输出0
    2.短字符串可能是长字符串的前缀

     其实这题都是基本的操作,不过需要注意细节(我自己调了5小时,因为有一处没有及时update...)

    /*
    
    分析:
        动态计算LCP。
    
        静态的话,可以用后缀数组来计算,但是对于动态的话就无能为力了。。。
        如果用询问来计算LCP的话,会TLE的,所以我们可以想到用二分+hash来计
        算。但是计算hash的话,由于是动态并且是区间统计问题,所以我们可以用
        splay节点表示字符,并且记录以该节点为根的子树所在的区间的字符串的
        hash值,判断是否相等就行了。。。
    
        hash函数的选取,用RKhash:
        hash[a,b] = ch[a]*1+ch[a+1]*27+...+ch[b]*27^(b-a)
    
        需要注意的是:
        1.对于首字符都不同的话,直接输出0
        2.短字符串可能是长字符串的前缀
    
    */
    #include <set>
    #include <map>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <string>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    typedef unsigned long long ull;
    
    #define debug puts("here")
    #define rep(i,n) for(int i=0;i<n;i++)
    #define REP(i,a,b) for(int i=a;i<b;i++)
    #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++)
    #define pb push_back
    #define RD(n) scanf("%d",&n)
    
    namespace Splay{
    
    #define lx ch[x][0]
    #define rx ch[x][1]
    #define px pre[x]
    
    #define ly ch[y][0]
    #define ry ch[y][1]
    #define py pre[y]
    
    #define lz ch[z][0]
    
    #define rt ch[root][1]
    #define lrt ch[rt][0]
    
        const int MAXN = 1e5+10;
        char str[MAXN];
        const int MOD = 9875321;
    
        int ch[MAXN][2],pre[MAXN],sz[MAXN];
        int ha[MAXN],val[MAXN];
        int p[MAXN];
        int root,tot;
    
        inline void update(int x){ // ok
            sz[x] = sz[lx]+sz[rx]+1;
            if(sz[0])   puts("error !!! sz[0] != 0");
            ha[x] = ll(ha[lx]+(ll)val[x]*p[ sz[lx] ]+(ll)ha[rx]*p[ sz[lx]+1 ])%MOD;
        }
    
        inline int sgn(int x){ // ok
            return ch[px][1]==x;
        }
        inline void setc(int y,int d,int x){ // ok
            ch[y][d] = x;
            px = y;
        }
    
        inline void rot(int x,int d){ // ok
            int y = px;
            int z = py;
            setc(y,!d,ch[x][d]);
            if(z)   setc(z,sgn(y),x);
            px = z;
            setc(x,d,y);
            update(y);
        }
    
        inline void splay(int x,int goal=0){ // ok
            if(!x)
                return;
            while(px!=goal){
                int y = px;
                int z = py;
                if(z==goal){
                    rot(x,!sgn(x));
                    break;
                }
                if(lz==y){
                    ly==x?rot(y,1):rot(x,0);
                    rot(x,1);
                }
                else{
                    ry==x?rot(y,0):rot(x,1);
                    rot(x,0);
                }
            }
            update(x);
            if(goal==0)
                root = x;
        }
    
        inline int get_Kth(int x,int k){ // ok
    
            int tmp = sz[lx]+1;
            if(tmp==k)  return x;
            return k<tmp?get_Kth(lx,k):get_Kth(rx,k-tmp);
        }
    
        inline int get_Min(int x){ // ok
            while(lx)   x = lx;
            return x;
        }
    
        inline void new_node(int &x,int y,int c){ // ok
            x = ++tot;
            ha[x] = val[x] = c;
            px = y;
            lx = rx = 0;
        }
    
        inline void build(int &x,int y,int l,int r){ // ok
            if(l>r) return;
            int mid = (l+r)>>1;
            new_node(x,y,str[mid]-'a'+1);
            build(lx,x,l,mid-1);
            build(rx,x,mid+1,r);
            update(x);
        }
    
        void dfs(int x){ // ok
            if(x){
                cout<<x<<" "<<lx<<" "<<rx<<" "<<sz[x]<<" "<<char(val[x]+'a'-1)<<endl;
                dfs(lx);
                dfs(rx);
            }
        }
    
        inline void init(){ // ok
            //memset(ch,0,sizeof(ch));
            //memset(sz,0,sizeof(sz));
            //memset(pre,0,sizeof(pre));
            //memset(ha,0,sizeof(ha));
    
            p[0] = 1;
            for(int i=1;i<MAXN;i++)
                p[i] = (ll)p[i-1]*27%MOD;
    
            root = tot = 0;
            new_node(root,0,0);
            new_node(rt,root,0);
    
            update(rt);
            update(root);
    
            gets(str);
            int n = strlen(str);
            build(lrt,rt,0,n-1);
            update(rt);
            update(root);
        }
    
        int has(int x,int y){ // ok
            splay( get_Kth(root,x) );
            splay( get_Kth(root,y+2),root );
            return ha[lrt];
        }
    
        inline void Q(){
            //puts("-------------------------------");
            int x,y;
            scanf("%d%d",&x,&y);
            if(x==y){
                printf("%d\n",tot-2-y+1);
                return;
            }
    
            if(x>y) swap(x,y);
    
            splay( get_Kth(root,x+1) );
            int tmp = val[root];
            splay( get_Kth(root,y+1) );
    
            //dfs(root);
            //cout<<"k = "<<get_Kth(root,y+1)<<endl;
    
            //cout<<"tot  = "<<tot<<endl;
            //cout<<dx<<" "<<dy<<endl;
            if(val[root] != tmp ){
                puts("0");
                return;
            }
            //cout<<"-------  "<<tot-2-y+1<<endl;
            if(tot-2-y+1==1){
                puts("1");
                return;
            }
            //debug;
    
            int len = tot-2-y;
            if( has(x,x+len)==has(y,y+len) ){
                printf("%d\n",tot-2-y+1);
                return;
            }
    
            //debug;
            int l = 1,r = tot-2-y;
            while(l<=r){
                int mid = (l+r)>>1;
                if(has(x,x+mid)==has(y,y+mid))
                    l = mid+1;
                else
                    r = mid-1;
            }
            printf("%d\n",l);
        }
    
        inline void R(){ // ok
            char s[2];
            int x;
            scanf("%d%s",&x,s);
            splay( get_Kth(root,x+1) );
            val[root] = s[0]-'a'+1;
            update(root);
        }
    
        inline void I(){ // ok
            int x;
            char s[2];
            scanf("%d%s",&x,s);
    
            x = get_Kth(root,x+1);
            //cout<<"dsadsa "<<x<<endl;
            splay(x);
            int y = get_Min(rt);
            splay(y,root);
            //cout<<y<<endl;
    
            new_node(lrt,rt,s[0]-'a'+1);
            update(lrt);
            update(rt);
            update(root);
            //dfs(root);
        }
    
        inline void solve(){ // ok
            init();
            //dfs(root);
            char op[2];
            int m;
            cin>>m;
            while(m--){
                scanf("%s",op);
                if(op[0]=='Q')      Q();
                else if(op[0]=='R') R();
                else                I();
            }
        }
    
    };
    
    int main(){
    
    #ifndef ONLINE_JUDGE
        freopen("sum.in","r",stdin);
        //freopen("sum.out","w",stdout);
    #endif
    
        Splay::solve();
    
        return 0;
    }
    

      

    BZOJ 1269 [AHOI2006]文本编辑器editor

    题目:支持 插入,翻转,删除的功能。
    分析:splay的基本操作。
    1.插入,我们需要把插入的位置伸展到根,然后再把伸展之后的后一个位置伸展到根的右儿子,插入就直接把所有的数插入到根的右儿子的左子树中。
    2.删除,我们把需要删除的整个区间[a,b]直接伸展到根的右儿子的左子树,即把a-1伸展到根,把b+1伸展到根的右儿子,然后把根的右儿子的左子树整个删除掉即可
    3.旋转,lazy标记,每当我们往下遍历的时候,需要把lazy标记下沉。而旋转过程中,比如我们需要对区间[a,b]旋转,我们可以把a-1旋转到根,把b+1旋转到根的右儿子,然后把根的右儿子的左子树的标记翻转一下就行了。
    4.输出,我们需要输出光标后的第一个字符,我们可以直接把光标的位置伸展到根,然后再求根的右儿子的最小值输出
    5.移动到第k个字符后面,我们直接把光标改变,无需把该位置伸展到根,只需要用的时候再进行伸展操作
    6.前移(后移),直接把光标减一(加一),无需进行伸展操作
    /*
    
    题目:支持 插入,翻转,删除的功能。
    分析:splay的基本操作。
        1.插入,我们需要把插入的位置伸展到根,然后再把伸展之后的后一个位置伸展到根的
        右儿子,插入就直接把所有的数插入到根的右儿子的左子树中。
        2.删除,我们把需要删除的整个区间[a,b]直接伸展到根的右儿子的左子树,即把a-1
        伸展到根,把b+1伸展到根的右儿子,然后把根的右儿子的左子树整个删除掉即可
        3.旋转,lazy标记,每当我们往下遍历的时候,需要把lazy标记下沉。而旋转过程中,比如
        我们需要对区间[a,b]旋转,我们可以把a-1旋转到根,把b+1旋转到根的右儿子,然后把根的
        右儿子的左子树的标记翻转一下就行了。
        4.输出,我们需要输出光标后的第一个字符,我们可以直接把光标的位置伸展到根,然后再
        求根的右儿子的最小值输出
        5.移动到第k个字符后面,我们直接把光标改变,无需把该位置伸展到根,只需要用的时候
        再进行伸展操作
        6.前移(后移),直接把光标减一(加一),无需进行伸展操作
    
    */
    #include <set>
    #include <map>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <string>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    typedef unsigned long long ull;
    
    #define debug puts("here")
    #define rep(i,n) for(int i=0;i<n;i++)
    #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++)
    #define pb push_back
    
    int tot,root,pos;
    
    namespace Splay{
    
    #define lx ch[x][0]
    #define rx ch[x][1]
    #define px pre[x]
    
    #define ly ch[y][0]
    #define ry ch[y][1]
    #define py pre[y]
    
    #define lz ch[z][0]
    #define rz ch[z][1]
    #define pz pre[z]
    
    #define rt ch[root][1]
    #define lrt ch[rt][0]
    
        const int X = (1<<21)+5;
    
        int ch[X][2],pre[X],sz[X],rev[X];
        char str[X];
        char qq[X];
    
        inline void dfs(int x){
            if(x){
                dfs(lx);
                cout<<lx<<" "<<rx<<" "<<x<<endl;
                dfs(rx);
            }
        }
    
        inline void update(int x){
            //cout<<lx<<" "<<rx<<" "<<sz[lx]<<" "<<sz[rx]<<" "<<sz[lx]+sz[rx]+1<<endl;
            sz[x] = sz[lx]+sz[rx]+1;
        }
    
        inline void push_down(int x){
            if(x&&rev[x]){
                swap(lx,rx);
                rev[lx] ^= 1;
                rev[rx] ^= 1;
                rev[x] = 0;
            }
        }
    
        inline void new_node(int &x,int y,char c){
            x = ++tot;
            pre[x] = y;
            rev[x] = ch[x][0] = ch[x][1] = 0;
            str[x] = c;
        }
    
        inline void build(int &x,int l,int r,int y,char *s){
            if(l>r) return;
            int mid = (l+r)>>1;
            new_node(x,y,s[mid]);
            build(lx,l,mid-1,x,s);
            build(rx,mid+1,r,x,s);
            update(x);
        }
    
        inline void setc(int y,int d,int x){
            ch[y][d] = x;
            pre[x] = y;
        }
    
        inline int sgn(int x){
            return ch[px][1]==x;
        }
    
        inline void _rot(int x,int d){
            int y = px;
            int z = py;
            push_down(y);
            push_down(x);
            setc(y,!d,ch[x][d]);
            if(z)   setc(z,sgn(y),x);
            pre[x] = z;
            setc(x,d,y);
            update(y);
        }
    
        inline void rot(int x){_rot(x,!sgn(x));}
        inline void zag(int x){_rot(x,0);}
        inline void zig(int x){_rot(x,1);}
    
    
        inline int splay(int x,int goal=0){
            push_down(x);
            while(px!=goal){
                int y = px;
                int z = py;
                if(z==goal){
                    rot(x);
                    break;
                }
                if(lz==y){
                    if(ly==x)
                        zig(y),zig(x);
                    else
                        zag(x),zig(x);
                }
                else{
                    if(ry==x)
                        zag(y),zag(x);
                    else
                        zig(x),zag(x);
                }
            }
            update(x);
            if(goal==0)
                root = x;
            return x;
        }
    
        inline int get_Kth(int x,int k){
            push_down(x);
            int tmp = sz[lx]+1;
            if(tmp==k)
                return x;
            if(k<tmp)
                return get_Kth(lx,k);
            else
                return get_Kth(rx,k-tmp);
        }
    
        inline int get_min(int x){
            push_down(x);
            while(lx){
                x = lx;
                push_down(x);
            }
            return x;
        }
    
        inline void Move(){
            int k;
            scanf("%d",&k);
            pos = k+1;
        }
    
        inline void Insert(){
            int k;
            scanf("%d",&k);
            getchar();
            gets(qq);
            int x = get_Kth(root,pos);
            splay(x);
            x = get_min(rt);
            splay(x,root);
            build(lrt,0,k-1,rt,qq);
        }
    
        inline void Delete(){
            int k;
            scanf("%d",&k);
            int x = get_Kth(root,pos);
            splay(x);
            int y = get_Kth(root,pos+k+1);
            splay(y,root);
    
            pre[lrt] = 0;
            lrt = 0;
            update(rt);
            update(root);
        }
    
        inline void Rotate(){
            int k;
            scanf("%d",&k);
            int x = get_Kth(root,pos);
            splay(x);
            int y = get_Kth(root,pos+k+1);
            splay(y,root);
            rev[lrt] ^= 1;
        }
    
        inline void Get(){
            int x = get_Kth(root,pos);
            splay(x);
            x = get_min(rt);
            printf("%c\n",str[x]);
        }
    
        inline void Prev(){
            pos --;
        }
    
        inline void Next(){
            pos ++;
        }
    
        inline void init(){
            memset(pre,0,sizeof(pre));
            memset(ch,0,sizeof(ch));
            memset(sz,0,sizeof(sz));
            memset(rev,0,sizeof(rev));
            root = tot = 0;
            char s[] = "@@@@@@";
            build(root,0,5,0,s);
            pos = 1;
        }
    
    }using namespace Splay;
    
    int main(){
    
    #ifndef ONLINE_JUDGE
    	freopen("sum.in","r",stdin);
    	//freopen("sum.out","w",stdout);
    #endif
    
        int ncase;
        char op[20];
        cin>>ncase;
        init();
        while(ncase--){
            scanf("%s",op);
            switch(op[0]){
                case 'M':Move();break;
                case 'I':Insert();break;
                case 'D':Delete();break;
                case 'R':Rotate();break;
                case 'G':Get();break;
                case 'P':Prev();break;
                default:Next();
            }
        }
    	return 0;
    }
    

      

    BZOJ 1500 [NOI2005]维修数列

    题目:
    插入、删除、区间赋值、翻转、求总和、求最大子序列
     
    分析:
    前面的都是跟文本编辑器一样的操作,而在求最大子序列的时候,我们需要维护lmax,rmax,mmax三个值,每次下沉的时候都需要更新
    需要建立内存池,把内存回收然后再利用。不然的话,内存不足以令数组开到那么大。

     

    /*
    
    题目:
        插入、删除、区间赋值、翻转、求总和、求最大子序列
    
    分析:
        前面的都是比较简单的splay操作,而在求最大子序列的时候,我们需要维护
        lmax,rmax,mmax三个值,每次下沉的时候都需要更新
    
    */
    #include <set>
    #include <map>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <string>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    typedef unsigned long long ull;
    
    #define debug puts("here")
    #define rep(i,n) for(int i=0;i<n;i++)
    #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++)
    #define pb push_back
    #define RD(n) scanf("%d",&n)
    
    
    namespace Splay{
    
    #define lx ch[x][0]
    #define rx ch[x][1]
    #define px pre[x]
    
    #define ly ch[y][0]
    #define ry ch[y][1]
    #define py pre[y]
    
    #define lz ch[z][0]
    
    #define rt ch[root][1]
    #define lrt ch[rt][0]
    
        const int MAXN = 500005;
        const int INF = 1e9;
    
        int n,m;
        int root,tot;
        int sta[MAXN],top;
        int pre[MAXN],ch[MAXN][2],sz[MAXN],val[MAXN];
        int sum[MAXN];
        int lmax[MAXN],rmax[MAXN],mmax[MAXN];
        bool rev[MAXN],same[MAXN];
        int aa[MAXN];
    
        inline void update_same(int x,int v){
            if(x==0)    return;
            val[x] = v;
            same[x] = true;
            sum[x] = sz[x]*v;
            mmax[x] = lmax[x] = rmax[x] = max(sum[x],v);
        }
    
        inline void update_rev(int x){
            if(x==0)    return;
            rev[x] ^= 1;
            swap(lx,rx);
            swap(lmax[x],rmax[x]);
        }
    
        inline void update(int x){
            sz[x] = sz[lx]+sz[rx]+1;
            sum[x] = sum[lx]+sum[rx]+val[x];
            lmax[x] = max( lmax[lx] , sum[lx]+val[x]+max(0,lmax[rx]) );
            rmax[x] = max( rmax[rx] , sum[rx]+val[x]+max(0,rmax[lx]) );
            mmax[x] = max( mmax[lx] , mmax[rx] );
            mmax[x] = max( mmax[x] , max(0,lmax[rx])+val[x]+max(0,rmax[lx]) );
        }
    
        inline void push_down(int x){
            if(rev[x]){
                update_rev(lx);
                update_rev(rx);
                rev[x] = 0;
            }
            if(same[x]){
                update_same(lx,val[x]);
                update_same(rx,val[x]);
                same[x] = 0;
            }
        }
    
        inline int sgn(int x){
            return ch[px][1]==x;
        }
    
        inline void setc(int y,int d,int x){
            ch[y][d] = x;
            px = y;
        }
    
        inline void rot(int x,int d){
            int y = px;
            int z = py;
            push_down(y);
            push_down(x);
            setc(y,!d,ch[x][d]);
            if(z)   setc(z,sgn(y),x);
            pre[x] = z;
            setc(x,d,y);
            update(y);
        }
    
        inline void splay(int x,int goal=0){
            push_down(x);
            while(px!=goal){
                int y = px;
                int z = py;
                if(z==goal){
                    rot(x,!sgn(x));
                    break;
                }
                if(lz==y){
                    if(ly==x)
                        rot(y,1),rot(x,1);
                    else
                        rot(x,0),rot(x,1);
                }
                else{
                    if(ry==x)
                        rot(y,0),rot(x,0);
                    else
                        rot(x,1),rot(x,0);
                }
            }
            update(x);
            if(goal==0)
                root = x;
        }
    
        inline int get_Kth(int x,int k){
            push_down(x);
            int tmp = sz[lx]+1;
            if(tmp==k)
                return x;
            if(k<tmp)
                return get_Kth(lx,k);
            return get_Kth(rx,k-tmp);
        }
    
        inline int get_min(int x){
            push_down(x);
            while(lx){
                x = lx;
                push_down(x);
            }
            return x;
        }
    
        inline void del(int x){
            if(x==0)    return;
            sta[top++] = x;
            del(lx);
            del(rx);
        }
    
        inline void Delete(){
            int pos,k;
            scanf("%d%d",&pos,&k);
            int x = get_Kth(root,pos);
            int y = get_Kth(root,pos+k+1);
            splay(x);
            splay(y,root);
            del(lrt);
            pre[lrt] = 0;
            lrt = 0;
            update(rt);
            update(root);
        }
    
        inline void Make_same(){
            int pos,k,v;
            scanf("%d%d%d",&pos,&k,&v);
    
            int x = get_Kth(root,pos);
            int y = get_Kth(root,pos+k+1);
            splay(x);
            splay(y,root);
    
            update_same(lrt,v);
            update(rt);
            update(root);
        }
    
        inline void Make_rev(){
            int pos,k;
            scanf("%d%d",&pos,&k);
    
            int x = get_Kth(root,pos);
            int y = get_Kth(root,pos+k+1);
    
            splay(x);
            splay(y,root);
    
            update_rev(lrt);
        }
    
        inline void new_node(int &x,int y,int v){
            if(top) x = sta[--top]; // 从内存池中取出编号
            else    x = ++tot;     //内存池为空
            ch[x][0] = ch[x][1] = 0;
            val[x] = sum[x] = lmax[x] = rmax[x] = mmax[x] = v;
            rev[x] = same[x] = 0;
            pre[x] = y;
        }
    
        inline void build(int &x,int y,int l,int r){
            if(l>r) return;
            int mid = (l+r)>>1;
            new_node(x,y,aa[mid]);
            build(lx,x,l,mid-1);
            build(rx,x,mid+1,r);
            update(x);
        }
    
        inline void Insert(){
            int pos,k;
            scanf("%d%d",&pos,&k);
            rep(i,k)
                RD(aa[i]);
    
            pos ++;
    
            int x = get_Kth(root,pos);
            splay(x);
            int y = get_min(rt);
            splay(y,root);
    
            build(lrt,rt,0,k-1);
            update(rt);
            update(root);
        }
    
        inline void Get_sum(){
            int pos,k;
            scanf("%d%d",&pos,&k);
            int x = get_Kth(root,pos);
            int y = get_Kth(root,pos+k+1);
            splay(x);
            splay(y,root);
            printf("%d\n",sum[lrt]);
        }
    
        inline void Get_max(){
            int pos = 1,k = sz[root]-2;
            int x = get_Kth(root,pos);
            int y = get_Kth(root,pos+k+1);
            splay(x);
            splay(y,root);
            printf("%d\n",mmax[lrt]);
        }
    
        inline void init(){
            root = tot = top = 0;
            ch[0][0] = ch[0][1] = 0;
            pre[0] = sz[0] = 0;
            sum[0] = same[0] = rev[0] = 0;
            lmax[0] = rmax[0] = mmax[0] = -INF;
    
            new_node(root,0,-1);
            new_node(rt,root,-1);
    
            update(rt);
            update(root);
    
            RD(n);
            RD(m);
            rep(i,n)
                RD(aa[i]);
    
            build(lrt,rt,0,n-1);
            update(rt);
            update(root);
        }
    
    }using namespace Splay;
    
    
    int main(){
    
    #ifndef ONLINE_JUDGE
    	freopen("sum.in","r",stdin);
    #endif
    
        init();
        char op[20];
        while(m--){
            scanf("%s",op);
            //puts(op);
            if(op[0]=='I')
                Insert();
            else if(op[0]=='D')
                Delete();
            else if(op[0]=='R')
                Make_rev();
            else if(op[0]=='G')
                Get_sum();
            else if(op[2]=='X')
                Get_max();
            else
                Make_same();
        }
    
    	return 0;
    }
    

      

  • 相关阅读:
    ELK相关操作记录-运维笔记
    php执行普通shell命令
    rsync 限速同步文件
    mysql 压测工具启动报 error while loading shared libraries: libmysqlclient.so.20解决办法
    php连接SQL server 数据库测试php脚本
    mysql 5.6.24 主从配置(增加从库)
    Mysqldump参数大全(参数来源于mysql5.5.19源码)
    Linux下mysql-5.6重置root密码
    WSDL文档深入分析
    随笔
  • 原文地址:https://www.cnblogs.com/yejinru/p/2934385.html
Copyright © 2011-2022 走看看