zoukankan      html  css  js  c++  java
  • poj_3580 伸展树

        自己伸展树做的第一个题 poj 3580 supermemo.

    题目大意

        对一个数组进行维护,包含如下几个操作:

    1. ADD x, y, d 在 A[x]--A[y] 中的每个数都增加d
    2. REVERSE x, y 将 A[x]--A[y] 中的数进行反转,变为 A[y],A[y-1]....A[x+1],A[x]
    3. REVOLVE x, y, T 将 A[x]--A[y]中的数连续右移T次
    4. INSERT x, P 在A后添加数P
    5. DELETE x 删除A[x]
    6. MIN x, y 查询A[x]--A[y]中的最小值

    思路

        对有序进行操作可选的数据结构有 线段树、treap、伸展树Splay等,这道题要求比较多,所以选用伸展树: 
        伸展树对数组进行维护的核心思想是,将需要维护的一组数单独提取出来,形成一棵子树(一般为整棵树的根节点的右子节点的左孩子节点 为根),然后再这个子树上进行操作。此时进行某些操作(如 ADD, REVERSE 等),只需要在根节点上做个标记,进行延迟处理(即在之后真正访问子节点时候才对子节点进行实际的更新操作),这样可以节省时间。 
        每次对树的节点进行修改(比如DELETE, INSERT等)之后,都要进行维护信息,此时需要Update一下,然后将该节点旋转至树根。 
        且在寻找一个区间的起始点对应在树中的节点的时候,都要将该节点所需要的所有信息带给该节点,这就要求在从根节点向下寻找该节点的时候,将路径上的所有节点(即该节点的祖先节点)上的标记都往下传,即PushDown。

    实现(c++)

    #define _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<string.h>
    #define MIN(a,b) a<b? a:b
    #define MAX_NODE_NUM 200005
    #define INFINITE 1 << 30
    
    struct TreeNode{
        int data; 
        int parent;
        int child[2];
        int child_dir;
    
        //程序相关的信息
        bool reverse;
        int min;
        int lazy;
        int size;
        //节点的索引为0,表示该节点为无效节点。若节点的parent = 0, 表示该节点为根节点,若节点的子节点为0,表示没有相应的子节点
        TreeNode(int d = INFINITE) :
            data(d), parent(0), child_dir(0), reverse(false), min(INFINITE), lazy(0), size(1){
            child[0] = child[1] = 0; 
        }
        void Reset(){
            parent = 0;
            child[0] = child[1] = 0;
            reverse = false;
            size = 1;
            lazy = 0;
            min = INFINITE;
        }
    };
    
    TreeNode gTreeNode[MAX_NODE_NUM];
    int gNumber[MAX_NODE_NUM];
    int gNodeCount;
    int gRootIndex;
    
    void LinkNode(int par, int ch, int dir){
        gTreeNode[par].child[dir] = ch;
        gTreeNode[ch].parent = par;
        gTreeNode[ch].child_dir = dir;
    }
    //维护本节点信息
    void Update(int node){
        gTreeNode[node].min = gTreeNode[node].data;
        gTreeNode[node].size = 1; 
        int left = gTreeNode[node].child[0], right = gTreeNode[node].child[1];
        if (left){
            gTreeNode[node].min = MIN(gTreeNode[node].min, gTreeNode[left].min);
            gTreeNode[node].size += gTreeNode[left].size;
        }
        if (right){
            gTreeNode[node].min = MIN(gTreeNode[node].min, gTreeNode[right].min);
            gTreeNode[node].size += gTreeNode[right].size;
        }
    }
    
    //向下更新信息
    void PushDown(int node){ 
        int left = gTreeNode[node].child[0];
        int right = gTreeNode[node].child[1];
        if (gTreeNode[node].reverse){
            LinkNode(node, left, 1);
            LinkNode(node, right, 0);
            gTreeNode[left].reverse ^= true;
            gTreeNode[right].reverse ^= true;
            gTreeNode[node].reverse = false;
        }
        int tmp_add = gTreeNode[node].lazy;
        if (tmp_add){
            gTreeNode[node].data += tmp_add;
    
            gTreeNode[left].min += tmp_add;
            gTreeNode[left].lazy += tmp_add;
            gTreeNode[right].min += tmp_add;
            gTreeNode[right].lazy += tmp_add;
    
            gTreeNode[node].lazy = 0;
        }
    }
    
    int BuildTree(int beg, int end){
        if (beg > end){
            return 0;
        }
        if (beg == end){
            gTreeNode[gNodeCount].data = gTreeNode[gNodeCount].min = gNumber[beg];
            return gNodeCount++;
        }
        int mid = (beg + end) / 2;
        int left = BuildTree(beg, mid - 1);
        int right = BuildTree(mid + 1, end);
    
        gTreeNode[gNodeCount].data = gTreeNode[gNodeCount].min = gNumber[mid];
        LinkNode(gNodeCount, left, 0);
        LinkNode(gNodeCount, right, 1);
        Update(gNodeCount);
        return gNodeCount++;
    }
    
    //zig or zag旋转
    void Rotate(int x){
        if (x == gRootIndex){
            return;
        }
        int y = gTreeNode[x].parent;
        PushDown(y);
        PushDown(x);
        int d = gTreeNode[x].child_dir;
    
        int z = gTreeNode[y].parent;
        LinkNode(z, x, gTreeNode[y].child_dir);
        LinkNode(y, gTreeNode[x].child[!d], d);
        LinkNode(x, y, !d);
        Update(y);
        if (y == gRootIndex){
            gRootIndex = x;
        }
    }
    
    //旋转操作,将node节点旋转到 f 节点下方
    void Splay(int x, int f){
        if (x == f){
            return;
        }
        PushDown(x);
        int y = gTreeNode[x].parent, z = 0;
        while (y != f){
            z = gTreeNode[y].parent;
            if (z == f){
                Rotate(x);
                break;
            }
            if (gTreeNode[x].child_dir == gTreeNode[y].child_dir){ //一字型旋转
                Rotate(y);
                Rotate(x);
            }
            else{ //之字形旋转
                Rotate(x);
                Rotate(x);
            }
            y = gTreeNode[x].parent;
        }
        Update(x);
    }
    //获取伸展树中 第k个节点的index
    int GetKthInTree(int k){
        int node = gRootIndex, left, tmp_size;
        while (node){
            PushDown(node); //注意要将与该节点有关的信息带下去
            left = gTreeNode[node].child[0];
            tmp_size = gTreeNode[left].size;
            if (!left){//left 为空节点
                if (k == 1){
                    return node;
                }
                else{
                    node = gTreeNode[node].child[1];
                    k--;
                    continue;
                }
            }
            if (tmp_size + 1 == k){
                return node;
            }
            else if (tmp_size >= k){
                node = left;
            }
            else{
                node = gTreeNode[node].child[1];
                k -= (tmp_size + 1);
            }        
        }
        return -1;
    }
    
    //选择区间,返回由该区间构成的子树的节点。节点为 根节点的右子节点的左子节点
    int SelectInterval(int x, int y){
        if (x <= 0 || y > gNodeCount){
            printf("fuck this splay tree!!!
    ");
            return -1;
        }
        if (x == 1 && y == gNodeCount - 1){
            return gRootIndex;
        }
        int node;
        if (x == 1){
            node = GetKthInTree(y + 1);
            Splay(node, 0);
            return gTreeNode[node].child[0];
        }
        if (y == gNodeCount - 1){
            node = GetKthInTree(x - 1);
            Splay(node, 0);
            return gTreeNode[node].child[1];
        }
        int node_beg = GetKthInTree(x - 1);
        Splay(node_beg, 0);
        int node_end = GetKthInTree(y + 1);
        Splay(node_end, gRootIndex);
    
        return gTreeNode[node_end].child[0];
    }
    void Add(int x, int y, int d){
        int node = SelectInterval(x, y);
        gTreeNode[node].min += d;
        gTreeNode[node].lazy += d;
        Splay(node, 0);
    }
    
    void Reverse(int x, int y){
        int node = SelectInterval(x, y);
        gTreeNode[node].reverse ^= true;  //注意是 ^= 而不是 直接 = (因为两次反转相当于不进行反转)
        Splay(node, 0);
    }
    
    void Revolve(int x, int y, int k){
        int w = y - x + 1;
        k = (k % w + w) % w;
        if (k == 0){
            return;
        }
        int node = SelectInterval(x, y);
        PushDown(node);
    
        int p = gTreeNode[node].parent;
        int node_x = GetKthInTree(x);
        Splay(node_x, p);
        PushDown(node_x);
    
        int node_y_sub_k = GetKthInTree(y - k);
        Splay(node_y_sub_k, node_x);
        PushDown(node_y_sub_k);
    
        int node_tmp = gTreeNode[node_y_sub_k].child[1];
        LinkNode(node_x, node_tmp, 0);
        gTreeNode[node_y_sub_k].child[1] = 0;
    
        //注意,node_y_sub_k 发生了改变,因此要更新
        Update(node_y_sub_k);
    
        Splay(node_tmp, 0);
    }
    
    void Insert(int x, int t){
        int node = SelectInterval(x, x);
        gTreeNode[gNodeCount].data = t;
        //将节点的信息push down,否则,如果该节点的信息没有被清除,在插入新节点后,可能会对新节点产生影响(因为新节点在该节点下方)
        PushDown(node);
    
        LinkNode(node, gNodeCount, 1);
    
        Splay(gNodeCount, 0);
        gNodeCount++;
    }
    void Delete(int x){
        int node = SelectInterval(x, x);
        PushDown(gTreeNode[node].parent);
    
        gNodeCount--;
        int p = gTreeNode[node].parent;
        gTreeNode[p].child[gTreeNode[node].child_dir] = 0; //去掉父节点的子节点
        //这里更改了,node,会导致node的父节点的信息发生改变,因此要进行维护!!!!
        Update(p);
        Splay(p, 0);
    
        p = gTreeNode[gNodeCount].parent;
        int left = gTreeNode[gNodeCount].child[0];
        int right = gTreeNode[gNodeCount].child[1];
    
        if (node == gNodeCount){
            gTreeNode[gNodeCount].Reset();
            return;
        }
    
        gTreeNode[node] = gTreeNode[gNodeCount];
        LinkNode(p, node, gTreeNode[gNodeCount].child_dir);
        if (p == 0){
            gRootIndex = node;  //可能会对根部造成改变
        }
        LinkNode(node, left, 0);
        LinkNode(node, right, 1);
    
        gTreeNode[gNodeCount].Reset();
    }
    
    int GetMin(int x, int y){
        int node = SelectInterval(x, y);
        //获得节点之后,一定要进行更新!!!
        PushDown(node);
        Update(node);
        return gTreeNode[node].min;
    }
    
    void debug(int node){
        if (node){
            debug(gTreeNode[node].child[0]);
    
            printf("node %d, parent = %d, left = %d, right = %d, data = %d, min = %d, lazy = %d, reverse = %d
    ",
                node, gTreeNode[node].parent, gTreeNode[node].child[0], gTreeNode[node].child[1],
                gTreeNode[node].data, gTreeNode[node].min, gTreeNode[node].lazy, gTreeNode[node].reverse);
    
            debug(gTreeNode[node].child[1]);
        }
    }
    int main(){
        int node_num;
        gNodeCount = 1;
        scanf("%d", &node_num);
        for (int i = 0; i < node_num; i++){
            scanf("%d", gNumber + i);
        }
    
        gRootIndex = BuildTree(0, node_num - 1); //递归的方式构造一棵开始就平衡的二叉树
        gTreeNode[gRootIndex].parent = 0; //
    
        int query_num;
        scanf("%d", &query_num);
        char op[10];
        int x, y, tmp;
    
        for (int i = 0; i < query_num; i++){
    //        debug(gRootIndex);
    
            scanf("%s", op);
    
            if (strcmp(op, "ADD") == 0){
                scanf("%d%d%d", &x, &y, &tmp);
                Add(x, y, tmp);
            }
            else if (strcmp(op, "REVERSE") == 0){
                scanf("%d%d", &x, &y);
                Reverse(x, y);
            }
            else if (strcmp(op, "REVOLVE") == 0){
                scanf("%d%d%d", &x, &y, &tmp);
                Revolve(x, y, tmp);
            }
            else if (strcmp(op, "INSERT") == 0){
                scanf("%d%d", &x, &tmp);
                Insert(x, tmp);
            }
            else if (strcmp(op, "DELETE") == 0){
                scanf("%d", &x);
                Delete(x);
            }
            else if (strcmp(op, "MIN") == 0){
                scanf("%d%d", &x, &y);
                printf("%d
    ", GetMin(x, y));
            }
            /*
            for (int i = 1; i < gNodeCount; i ++){
                printf("%d ", GetMin(i, i));
            }
            printf("
    
    ");
            */
        }
        return 0;
    }
  • 相关阅读:
    【web前端面试题整理03】来看一点CSS相关的吧
    【web前端面试题整理02】前端面试题第二弹袭来,接招!
    【web前端优化之图片模糊到清晰】看我QQ空间如何显示相片
    【web前端面试题整理01】各位加班累了吧,来做点前端面试题吧
    【javascript激增的思考03】MVVM与Knockout
    【javascript激增的思考02】模块化与MVC
    【javascript激增的思考01】模块化编程
    【position也可以很复杂】当弹出层遇上了鼠标定位(下)
    【position也可以很复杂】当弹出层遇上了鼠标定位(上)
    iOS开发拓展篇—音乐的播放
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4713871.html
Copyright © 2011-2022 走看看