zoukankan      html  css  js  c++  java
  • 伸展树基本概念基本题目

    http://blog.csdn.net/discreeter/article/details/51524210   //基本概念详见这里

    例题HDU4453 代码来源http://blog.csdn.net/auto_ac/article/details/12318809

    伸展树我个人理解就是每次查询或更改都要将其移动至根节点 另外伸展树有单点操作和区间操作 维护的是一个中序遍历(这点很重要)

    旋转操作的话结合概念和代码还是很清晰的,有左旋(当要进行旋转操作的是其根节点的右节点),右旋(当要进行旋转操作的是其根节点的左节点),还有一字型旋转和之字形旋转,详见基本概念

    情况一:节点x的父节点y是根节点。这时,如果x是y的左孩子,我们进行一次Zig(右旋)操作;如果x 是y 的右孩子,则我们进行一次Zag(左旋)操作。经过旋转,x成为二叉查找树S的根节点,调整结束。即:如果当前结点父结点即为根结点,那么我们只需要进行一次简单旋转即可完成任务,我们称这种旋转为单旋转。如图1所示

     

    (图1)

    情况二:节点x 的父节点y 不是根节点,y 的父节点为z,且x 与y 同时是各自父节点的左孩子或者同时是各自父节点的右孩子。这时,我们进行一次Zig-Zig操作或者Zag-Zag操作。即:设当前结点为X , X 的父结点为Y ,Y 的父结点为Z ,如果Y 和X 同为其父亲的左孩子或右孩子,那么我们先旋转Y ,再旋转X 。我们称这种旋转为一字形旋转。如图2所示

    (图2)

    情况三:节点x的父节点y不是根节点,y的父节点为z,x与y中一个是其父节点的左孩子而另一个是其父节点的右孩子。这时,我们进行一次Zig-Zag操作或者Zag-Zig 操作。即:这时我们连续旋转两次X 。我们称这种旋转为之字形旋转。如图3所示

    (图3)

    如图4所示,执行Splay(1,S),我们将元素1 调整到了伸展树S 的根部。再执行Splay(2,S),如图5 所示,我们从直观上可以看出在经过调整后,伸展树比原来“平衡”了许多。而伸展操作的过程并不复杂,只需要根据情况进行旋转就可以了,而三种旋转都是由基本得左旋和右旋组成的,实现较为简单。

    (图4)

    (图5)

    区间操作的图解原网站也很清晰:

    首先我们认为伸展树的中序遍历即为我们维护的数列,那么很重要的一个操作就是怎么在伸展树中表示任意一个区间。比如我们要提取区间a,b],那么我们将a前面一个数对应的结点转到树根,将b 后面一个结点对应的结点转到树根的右边,那么根右边的左子树就对应了区间[a,b]。其中的道理也是很简单的,将a 前面一个数对应的结点转到树根后, a 及a 后面的数就在根的右子树上,然后又将b后面一个结点对应的结点转到树根的右边,那么[a,b]这个区间就是图8中*所示的子树。

    利用这个,我们就可以实现线段树的一些功能,比如回答对区间的询问。我们在每个结点上记录关于以这个结点为根的子树的信息,然后询问时先提取区间,再直接读取子树的相关信息。还可以对区间进行整体修改,这也要用到和线段树类似的延迟标记技术,就是对于每个结点,再额外记录一个或多个标记,表示以这个结点为根的子树是否被进行了某种操作,并且这种操作影响其子结点的信息值。当然,既然记录了标记,那么旋转和其他一些操作中也就要相应地将标记向下传递。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define KT ch[ ch[root][1] ][0]
    #define L ch[x][0]
    #define R ch[x][1]
    const int maxn = 300005;
    int num[maxn], n, m;
    int k1, k2;
    typedef long long ll;
    struct splayTree {
        int ch[maxn][2], sz[maxn], pre[maxn];
        int root, tot, all; // all:节点总数, tot:最大标号

        int add[maxn], val[maxn];
        bool flip[maxn]; //翻转标记
        int sta[maxn], top;
        void rotate(int &x, int f) {//f等于一是右旋
               int y = pre[x], z = pre[y];
               down(y); down(x);
               ch[y][!f] = ch[x][f];
               pre[ch[x][f]] = y;
               pre[x] = pre[y];
               if(z) ch[z][ch[z][1] == y] = x;
               ch[x][f] = y;
               pre[y] = x;
               up(y);
           }
           void splay(int &x, int g) {
               down(x);
               while(pre[x] != g) {
                   int y = pre[x], z = pre[y];
                   down(z); down(y); down(x);
                   if(z == g) {
                       rotate(x, ch[y][0] == x);
                   }
                   else {
                       int f = (ch[z][0] == y);
                       ch[y][!f] == x ? rotate(y, f) : rotate(x, !f);
                       rotate(x, f);
                   }
               }
               if(!g) root = x;
               up(x);
           }
           void rto(int k, int g) {
               int x = root;
               while(1) {
                   down(x);

                   if(sz[L] == k) break;
                   if(sz[L] > k) x = L;
                   else {
                       k -= sz[L] + 1;
                       x = R;
                   }
               }
               splay(x, g);
           }
        void down(int x){
            if(add[x]) {
                val[L] += add[x];
                val[R] += add[x];
                add[L] += add[x];
                add[R] += add[x];
                add[x] = 0;
            }
            if(flip[x]) {
                flip[L] ^= 1;
                flip[R] ^= 1;
                swap(L, R);
                flip[x] = 0;
            }
        }
        void up(int x) {
            sz[x] = sz[L] + sz[R] +1;
        }

        void build(int &x, int l, int r, int fa) {
            if(l > r) return;
            int m = (l+r) >> 1;
            newNode(x, num[m], fa);
            build(L, l, m-1, x);
            build(R, m+1, r, x);
            up(x);
        }
        void newNode(int &x, int v, int fa) {
            if(top) x = sta[top--]; //内存回收
            else x = ++tot;
            all++;
            pre[x] = fa;
            sz[x] = 1;
            L = R = 0;
            val[x] = v;
            add[x] = 0;
            flip[x] = 0;
        }
        void init(int n) {
            all = tot = top = 0;
            newNode(root, 0, 0);
            // all:节点总数, tot:最大标号
            newNode(ch[root][1], 0, root);
            for(int i = 0; i < n; i++)
                scanf("%d", &num[i]);
            build(KT, 0, n-1, ch[root][1]);
            up(ch[root][1]);
            up(root);
            debug();
        }
        ll erase(int k) { //删除第k个数
            rto(k, 0);
            rto(k-1, root);
            sta[++top] = root;
            all--;
            ll ret = val[root];
            int ls = ch[root][0], rs = ch[root][1];
            root = ls;
            pre[ls] = 0;
            ch[ls][1] = rs;
            if(rs)pre[rs] = ls;
            up(root);
            return ret;
        }
        void insert(int k, int v) { //在第k个数后面插入一个数v
            rto(k, 0);
            int x;
            int rs = ch[root][1];
            newNode(x, v, root);
            ch[root][1] = x;
            ch[x][1] = rs;
            if(rs) pre[ch[x][1]] = x;
            up(ch[root][1]);
            up(root);
        }
        void move1() {
            int v = erase(all-2);
            insert(0, v);
        }
        void move2() {
            int v = erase(1);
            insert(all-2, v);

        }
        void update(int l, int r, int v) {
            rto(l-1, 0);
            debug();
            rto(r+1, root);
            debug();
            val[KT] += v;
            add[KT] += v;
            up(ch[root][1]);
            up(root);
        }
        void reverse(int l, int r) {
            rto(l-1, 0);
            debug();
            rto(r+1, root);
            debug();
            flip[KT] ^= 1;
            up(ch[root][1]);
            up(root);
        }
        int query() {
            rto(1, 0);
            return val[root];
        }
        void print(int x) {
            printf("node %d: ls=%d rs=%d lsz = %d rsz = %d val = %d ", x, L, R, sz[L], sz[R], val[x]);
            if(L)print(L);
            if(R)print(R);
        }
        void debug() {
            printf("root = %d ", root);
             print(root);
        }
    }spt;
    int main() {
        int ca = 1;
        while(~scanf("%d%d%d%d", &n, &m, &k1, &k2)) {
            if(!m && !n && !k1 && !k2) break;
            char op[111];
            spt.init(n);
          //spt.debug();
            printf("Case #%d: ", ca++);
            while(m--) {
                scanf("%s", op);
                if(op[0] == 'q') {
                    printf("%d ", spt.query());
                    spt.debug();
                }
                if(op[0] == 'm') {
                    int kd;
                    scanf("%d", &kd);
                    if(kd == 1) spt.move1();
                    else spt.move2();
                    spt.debug();
                }
                if(op[0] == 'i') {
                    int v;
                    scanf("%d", &v);
                    spt.insert(1, v);
                    spt.debug();
                }
                if(op[0] == 'd')
                    {spt.erase(1);spt.debug();}
                if(op[0] == 'a') {
                    int v;
                    scanf("%d", &v);
                    spt.update(1, k2, v);
                    spt.debug();
                }
                if(op[0] == 'r')
                    spt.reverse(1, k1);
            }
        }
        return 0;
    }

  • 相关阅读:
    FastDFS介绍
    SwiftUI 中使用SDWebImageSwiftUI加载网络图片
    SwiftUI 中使用BBSwiftUIKit开源库实现上拉加载和下拉刷新
    SwiftUI 中使用ScrollView+LazyVStack代替List
    SwiftUI 动画
    SwiftUI 中实现省市区选择器
    SwiftUI 中Slider的使用
    SwiftUI 中Stepper的使用
    SwiftUI 中通过Toggle实现单选框和复选框效果
    SwiftUI 中加载bundle中的图片
  • 原文地址:https://www.cnblogs.com/mfys/p/6915480.html
Copyright © 2011-2022 走看看