• bzoj 1588: [HNOI2002]营业额统计(splay入门)


    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1588

    题解:这题如果用普通的bst的话是可以过时间差不多4s左右如果用splay的话是140ms。

    由于splay可以有序的插入,各种操作都不会改变中序遍历的结果,而且splay每次可以将插入的位置通过旋转到根的位置,那么这题就可以通过寻找根节点的前驱最大值和后继最小值找到答案而且非常快速。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cstdlib>
    #define inf 0X3f3f3f3f
    using namespace std;
    const int M = 1e5 + 10;
    int pre[M] , key[M] , ch[M][2] , root , tot;
    int n;
    void NewNode(int &r , int fa , int k) {
        r = ++tot;
        pre[r] = fa;
        key[r] = k;
        ch[r][0] = ch[r][1] = 0;
    }//初始化节点
    void Rotate(int x , int kind) {
        int y = pre[x];
        ch[y][!kind] = ch[x][kind];
        pre[ch[x][kind]] = y;
        if(pre[y]) ch[pre[y]][ch[pre[y]][1] == y] = x;
        pre[x] = pre[y];
        ch[x][kind] = y;
        pre[y] = x;
    }//旋转操作一般不改变
    void Splay(int r , int goal) {
        while(pre[r] != goal) {
            if(pre[pre[r]] == goal) Rotate(r , ch[pre[r]][0] == r);
            else {
                int y = pre[r];
                int kind = (ch[pre[y]][0] == y);
                if(ch[y][kind] == r) {
                    Rotate(r , !kind);
                    Rotate(r , kind);
                }
                else {
                    Rotate(y , kind);
                    Rotate(r , kind);
                }
            }
        }
        if(goal == 0) root = r;
    }//splay操作一般不改变就是将r点移到goal的子树
    void init(int n) {
        tot = root = 0;
        for(int i = 0 ; i <= n ; i++) {
            pre[i] = 0 , key[i] = 0 , ch[i][0] = ch[i][1] = 0;
        }
    }
    int Insert(int k) {
        int r = root;
        while(ch[r][key[r] <= k]) {
            if(key[r] == k) {
                Splay(r , 0);
                return 0;
            }//相同的点不插入。
            r = ch[r][key[r] <= k];
        }
        NewNode(ch[r][key[r] <= k] , r , k);
        Splay(ch[r][key[r] <= k] , 0);
        return 1;
    }//插入节点依据点的权值插左右子树
    int get_pre(int x) {
        int tmp = ch[x][0];
        if(tmp == 0) return inf;
        while(ch[tmp][1]) tmp = ch[tmp][1];
        return key[x] - key[tmp];
    }//寻找前驱最接近的点。
    int get_next(int x) {
        int tmp = ch[x][1];
        if(tmp == 0) return inf;
        while(ch[tmp][0]) tmp = ch[tmp][0];
        return key[tmp] - key[x];
    }//寻找后继最接近的点。
    int main() {
        int n , x , ans = 0;
        scanf("%d" , &n);
        scanf("%d" , &x);
        init(n);
        ans += x;
        Insert(x);
        n--;
        while(n--) {
            scanf("%d" , &x);
            if(Insert(x))
                ans += min(get_pre(root) , get_next(root));
        }
        printf("%d
    " , ans);
        return 0;
    }
  • 相关阅读:
    HTML5和CSS3基础教程(第8版)-读书笔记(3)
    HTML5和CSS3基础教程(第8版)-读书笔记(2)
    HTML5和CSS3基础教程(第8版)-读书笔记
    JavaScript高级程序设计-读书笔记(7)
    HTTP状态码列表
    vue 监听对象里的特定数据
    vue 项目中命名方法
    一些常用文件夹和类的一些命名
    点将产品前端架构重构
    常用正则表达式总结
  • 原文地址:https://www.cnblogs.com/TnT2333333/p/7201124.html
Copyright © 2020-2023  润新知