zoukankan      html  css  js  c++  java
  • [2017.9.18] BZOJ3223Tyvj 1729 文艺平衡树(Splay区间翻转)

    直到今天考试才发现自己忘记Splay区间翻转操作了……

    1.如何建树?

    每个节点除了传统Splay所要维护的左右儿子,父亲,子树大小之外,还要维护一个权值,代表在初始数列的值,还要维护一个bool标记,表示自己是否被翻转了。建树时,按照中序遍历为原序列,类似二分的思想建树。

    2.什么是最终序列?

    每个点的左子树的size+1即为该节点在该序列的位置(即下标),而每个节点的权值就是那个位置的值,所以最终答案就是最终的Splay的中序遍历。

    3.对于翻转操作,如何翻转序列?

    1)如何找到区间?

    要多开两个点:0号点和n+1号点,否则会出现问题,统计答案时忽略就好了

    将所求区间的左端点位置的点旋转到根,(右端点+2)位置的点旋转到根节点的右儿子,此时,根节点的右儿子的左子树包含的点就是所求区间的点,不要问我为什么,我不能理性解释,就当结论记住吧。这里注意,splay不一定要把节点旋转到根,事实上,旋转到任意父亲节点都是可以的,旋转到根就相当于旋转到0号点的儿子。

    2)如何实现翻转?

    其实翻转就是把左右子树翻转,因为本来位置就是通过左右儿子表现出来的,一棵树的左右左右儿子翻转,就相当于给区间左右翻转。

    但是直接把子树所有节点的左右儿子翻转复杂度是不对的。可以发现,有很多区间翻转过来又翻转过去,每次翻转是极大的浪费。考虑引入“lazy”的思想,如果要修改一颗子树,就把根节点打上lazy标记,下方就是把左右儿子翻转,并把标记下方给左右儿子。上放就是统计size,即size[now]=size[ls]+size[rs]+1;

    所以这就要求我们每访问一个节点都要下放和上放。别的操作都好处理,关键是splay操作,这里我们要开一个栈,把要splay的节点到目标节点路径上的所有点用栈存下来,再从上往下下放标记,对于每次rotate,都要上放,最后本身当然也要上放。

    复杂度O(mlog(n))

    其实就这么多,上面已经讲的比较清楚了,还有一些细节(比如旋转完root要单独上放一次,rotate上放的是f而不是x)代码可能更清楚。裸题BZOJ3223: Tyvj 1729 文艺平衡树

     1 #include<bits/stdc++.h>
     2 #define N 100010
     3 #define RG register
     4 #define inf 0x3f3f3f3f
     5 #define ls dot[x].son[0]
     6 #define rs dot[x].son[1]
     7 using namespace std;
     8 int m,n,rt,x1,x2,tmp,top,tot,sta[N];
     9 struct node{
    10     bool tag;
    11     int fa,siz,val,son[2];
    12     inline void init(RG int x){siz=1;val=x;}
    13 }dot[N];
    14 inline int gi(){
    15     RG int x=0;RG char c=getchar();
    16     while(c<'0'||c>'9') c=getchar();
    17     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    18     return x;
    19 }
    20 inline void push_up(RG int x){
    21     dot[x].siz=dot[ls].siz+dot[rs].siz+1;
    22 }
    23 inline int build(RG int l,RG int r){
    24     if(l>r) return 0;
    25     RG int x=++tot,mid=(l+r)>>1;
    26     dot[x].init(mid);
    27     ls=build(l,mid-1);
    28     rs=build(mid+1,r);
    29     dot[ls].fa=dot[rs].fa=x;
    30     push_up(x);
    31     return x;
    32 }
    33 inline void push_down(RG int x){
    34     if(dot[x].tag){
    35     swap(ls,rs);
    36     dot[x].tag=0;
    37     dot[ls].tag^=1;
    38     dot[rs].tag^=1;
    39     }
    40 }
    41 inline int find(RG int x,RG int g){//寻找某个点
    42     push_down(x);
    43     if(dot[ls].siz+1<g)  return find(rs,g-1-dot[ls].siz);
    44     if(dot[ls].siz+1==g) return x;
    45     return find(ls,g);
    46 }
    47 inline bool dir(RG int x){
    48     return dot[dot[x].fa].son[1]==x;
    49 }
    50 inline void rotate(RG int x){
    51     RG bool op=dir(x);
    52     RG int f=dot[x].fa;
    53     dot[x].fa=dot[f].fa;
    54     dot[dot[f].fa].son[dir(f)]=x;
    55     dot[dot[x].son[op^1]].fa=f;
    56     dot[f].son[op]=dot[x].son[op^1];
    57     dot[f].fa=x;
    58     dot[x].son[op^1]=f;
    59     push_up(f);
    60 }
    61 inline void splay(RG int x,RG int g){//将x旋转到g操作,0表示旋转到根
    62     top=0;tmp=x;
    63     while(tmp!=g){
    64     sta[++top]=tmp;//用栈来维护原x到g路径上的点,这下点从上往下都要下放
    65     tmp=dot[tmp].fa;
    66     }
    67     for (RG int i=top;i;--i)
    68     push_down(sta[i]); 
    69     if(!g) rt=x; 
    70     while(dot[x].fa!=g){
    71     if(dot[dot[x].fa].fa==g)  {rotate(x);break;}
    72     if(dir(x)==dir(dot[x].fa)) rotate(dot[x].fa),rotate(x);
    73     else                       rotate(x),rotate(x);
    74     }
    75     push_up(x);
    76 }
    77 inline void dfs(RG int x){//统计答案,一定要记得先下放!!!
    78     push_down(x);
    79     if(ls) dfs(ls);
    80     if(dot[x].val&&dot[x].val<=n)
    81         printf("%d ",dot[x].val);
    82     if(rs) dfs(rs);
    83 }
    84 int main(){
    85     n=gi();m=gi();
    86     rt=build(0,n+1);//一定要记得多开0、n+1两个点
    87     while(m--){
    88     RG int l=gi();RG int r=gi();
    89     x1=find(rt,l);x2=find(rt,r+2);
    90     splay(x1,0);splay(x2,x1);push_up(rt);//记得rt要上放,其实所有非旋转到根的旋转,最终节点到根节点的链上都要上放
    91     dot[dot[x2].son[0]].tag^=1;//改变标记
    92     }
    93     dfs(rt);
    94     putchar('
    ');
    95     return 0;
    96 }
    View Code
  • 相关阅读:
    两个数据库比较 对比视图存储过程及表结构差异
    导入/导出Excel
    比较两个数据库的表结构差异
    根据当月数据库自动生成下个月数据库--3
    根据当月数据库自动生成下个月数据库--2
    根据当月数据库自动生成下个月数据库--1
    ubuntu 分屏工具
    ubuntu 分屏工具
    中英文对照 —— 色彩的描述
    中英文对照 —— 色彩的描述
  • 原文地址:https://www.cnblogs.com/Super-Nick/p/7544745.html
Copyright © 2011-2022 走看看