Splay区间翻转(BZOJ3223)

#include <cstdio>
#include <iostream>
using namespace std;

  const int nil=2*1e5;//nil表示不存在的节点
  int son[200001][2],flag[200001],size[200001],v[200001];
  int n,m;
  int root,cnt,fa[200001];

  inline void swap(int &a,int &b){
      int t=a;
      a=b;b=t;
  }

  inline void mark(int po){
      swap(son[po][0],son[po][1]);
      flag[po]^=1;
  }

  inline void pushdown(int po){
      int l=son[po][0],r=son[po][1];
      if (flag[po]){
      if (l!=nil) mark(l);
      if (r!=nil) mark(r);
      flag[po]^=1;
    }
  }

  inline int find(int num){
      int po=root;
      while (1){
        pushdown(po);
        if (size[son[po][0]]+v[po]==num) {return(po);continue;}
        if (size[son[po][0]]+v[po]>num)  {po=son[po][0];continue;}
        if (size[son[po][0]]+v[po]<num)  {num-=size[son[po][0]]+v[po],po=son[po][1];continue;}
    }
  }

  int build(int l,int r){
      int mid=(l+r)>>1;
      size[mid]=min(r,n)-max(l,1)+1;
      son[mid][0]=son[mid][1]=nil;

      if (l<mid){
        son[mid][0]=build(l,mid-1);
        fa[son[mid][0]]=mid;
      }

      if (r>mid){
        son[mid][1]=build(mid+1,r);
        fa[son[mid][1]]=mid;
      } 

    return(mid);
  }//建树时加入权值为0的0号点,权值为1的n+1号点,保证翻转l=1或r=n+1的操作正常进行

  void rotate(int po,int s){
      int f=fa[po];
      pushdown(po);
      son[f][s]=son[po][s^1];
      if (son[po][s^1]!=nil) fa[son[po][s^1]]=f;
      if (f==root) root=po;else
        if (f==son[fa[f]][0]) son[fa[f]][0]=po;else
        son[fa[f]][1]=po;
      fa[po]=fa[f];
      fa[f]=po;
      son[po][s^1]=f;
      size[f]=size[son[f][0]]+size[son[f][1]]+v[f];
      size[po]=size[son[po][0]]+size[son[po][1]]+v[po];
  }

  void splay(int po,int targ){
      while (fa[po]!=targ){
        if (fa[fa[po]]==targ) {rotate(po,(po==son[fa[po]][1]));continue;}
        int u,v;
        if (po==son[fa[po]][1]) u=1;else u=-1;
        if (fa[po]==son[fa[fa[po]]][1]) v=1;else v=-1;
        if (u*v==1){
            rotate(fa[po],(fa[po]==son[fa[fa[po]]][1]));
            rotate(po,(po==son[fa[po]][1]));
      };
      if (u*v==-1){
          rotate(po,(po==son[fa[po]][1]));
          rotate(po,(po==son[fa[po]][1]));
      }
    }
    size[po]=size[son[po][0]]+size[son[po][1]]+v[po];
  }

  void reverse(int l,int r){
      int p=find(l-1);
    splay(p,nil);
      p=find(r+1);
    splay(p,root);
      mark(son[son[root][1]][0]);
  }

  int main(){
      freopen("a.in","r",stdin);

      scanf("%d%d",&n,&m);

      for (int i=1;i<=n;i++) v[i]=1;
      v[0]=0;v[n+1]=1;
      root=build(0,n+1);
      fa[root]=nil;

      for (int i=1;i<=m;i++){
        int l,r;
        scanf("%d%d",&l,&r);
        reverse(l,r);
    }

    for (int i=1;i<=n;i++) printf("%d ",find(i));
  }
时间: 2024-10-08 02:53:36

Splay区间翻转(BZOJ3223)的相关文章

UVA 11922 Splay区间翻转+分裂+合并

- Permutation Transformer Time Limit:2000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Practice UVA 11922 Appoint description:  System Crawler  (2014-11-30) Description  Permutation Transformer  Write a program to transform th

HDU 1890 Splay区间翻转

D - Robotic Sort Time Limit:2000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 1890 Appoint description:  System Crawler  (2014-11-27) Description Somewhere deep in the Czech Technical University buildings, t

HDU3487(splay区间翻转+区间切割)

题意:开始有一个1,2,3,...n的序列,进行m次操作,cut a b c将区间[a,b]取出得到新序列,将区间插入到新序列第c个元素之后.filp a b 将区间a,b翻转,输出最终的序列. 思路:对于cut操作我们需要先提取出区间[a,b]然后,先暂时分裂出去,然后以c为边界分裂左右两部分,然后合并左边的和区间[a,b],将最大的旋转至根,然后和右边的合并.对于filp操作,我们可以使用lazy标记,先不翻转,需要的时候再翻转,代码如下 [cpp] view plaincopy /****

11922 - Permutation Transformer (Splay区间翻转)

UVA 11922 - Permutation Transformer 题目链接 题意:给一个序列,每次操作选择(a,b),把(a,b)序列翻转之后加到末尾,求最终序列 思路:Splay的应用,多一个flip标记,在开头多一个虚拟的0结点,这样每次就利用Splay进行分裂合并即可 代码: #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include

HDU 3487 splay区间翻转切割

Play with Chain Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3986    Accepted Submission(s): 1633 Problem Description YaoYao is fond of playing his chains. He has a chain containing n diamon

hdu 1890 Robotic Sort(splay 区间反转+删点)

题目链接:hdu 1890 Robotic Sort 题意: 给你n个数,每次找到第i小的数的位置,然后输出这个位置,然后将这个位置前面的数翻转一下,然后删除这个数,这样执行n次. 题解: 典型的splay区间翻转+删点. 我们把数据排序,然后记录一下每个数原来的位置,然后splay建树的时候用原来的位置来对应,这样val[i].second就直接是这个数在splay中的那个节点. (当然你也可以普通建树,然后手动记录位置). 然后我们把要找的那个数对应的节点旋转到根,然后根左边的size+i就

算法模板——splay区间反转 2

实现功能:同splay区间反转 1(基于BZOJ3223 文艺平衡树) 这次改用了一个全新的模板(HansBug:琢磨了我大半天啊有木有),大大简化了程序,同时对于splay的功能也有所完善 这里面没有像一般二叉排序树那样子用一个参量进行排序,而是直接以中序遍历来构建了一个普通的二叉树(当然也可以把每个点的中序遍历排名视作参量),然后插入的时候就是指定位置插入(这个就比较像是文本插入了) 总之得到了较大的提升,代码优美程度也提高不少 1 var 2 i,j,k,l,m,n,head,tot,ll

WIKIOI 3243 区间翻转

3243 区间翻转 题目描述 Description 给出N个数,要求做M次区间翻转(如1 2 3 4变成4 3 2 1),求出最后的序列 输入描述 Input Description 第一行一个数N,下一行N个数表示原始序列,在下一行一个数M表示M次翻转,之后的M行每行两个数L,R表示将区间[L,R]翻转. 输出描述 Output Description 一行N个数 , 表示最终序列. 样例输入 Sample Input 4 1 2 3 4 2 1 2 3 4 样例输出 Sample Outp

区间翻转

时间限制: 1 s空间限制: 256000 KB题目等级 : 钻石 Diamond 题目描述 Description 给出N个数,要求做M次区间翻转(如1 2 3 4变成4 3 2 1),求出最后的序列 输入描述 Input Description 第一行一个数N,下一行N个数表示原始序列,在下一行一个数M表示M次翻转,之后的M行每行两个数L,R表示将区间[L,R]翻转. 输出描述 Output Description 一行N个数 , 表示最终序列. 样例输入 Sample Input 4 1