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;
    }
  • 相关阅读:
    django页面分类和继承
    django前端从数据库获取请求参数
    pycharm配置django工程
    django 应用各个py文件代码
    CF. 1428G2. Lucky Numbers(背包DP 二进制优化 贪心)
    HDU. 6566. The Hanged Man(树形背包DP DFS序 重链剖分)
    小米邀请赛 决赛. B. Rikka with Maximum Segment Sum(分治 决策单调性)
    区间树 学习笔记
    CF GYM. 102861M. Machine Gun(主席树)
    2016-2017 ACM-ICPC East Central North America Regional Contest (ECNA 2016) (B, D, G, H)
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4713871.html
Copyright © 2011-2022 走看看