BZOJ 1588:splay tree

刚学习的splay tree。照着大神的代码敲了敲,理解了个大概

很好用的数据结构,可以用来维护数列

学习时建议先看看SBT,这样可以更好地理解“旋转”

#include"cstdio"
#include"queue"
#include"cmath"
#include"stack"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"queue"
#include"map"
#include"vector"
#define ll long long
#define mems(a,b) memset(a,b,sizeof(a))

using namespace std;
const int MAXN = 1e5+50;
const int MAXE = 200500;
const int INF = 0x3f3f3f;

int pre[MAXN],val[MAXN],ch[MAXN][2];///父亲结点、值、孩子节点(0左1右)
int tot,root;///总结点数、根节点
///中序遍历为所维护数列

void newnode(int &pos,int fa,int w){
    pos=++tot;
    pre[pos]=fa;
    val[pos]=w;
    ch[pos][0]=ch[pos][1]=0;
}

void Rotate(int x,int kind){///0左1右
    int fa=pre[x];
    ch[fa][!kind]=ch[x][kind];
    pre[ch[x][kind]]=fa;

    if(pre[fa]) ch[pre[fa]][ch[pre[fa]][1]==fa]=x;
    pre[x]=pre[fa];

    ch[x][kind]=fa;
    pre[fa]=x;
}

void Splay(int r,int goal){///将r结点旋转至goal下方
    while(pre[r]!=goal){
        if(pre[pre[r]]==goal) Rotate(r,ch[pre[r]][0]==r);
        else{
            int fa=pre[r];
            int kind=ch[pre[fa]][0]==fa;
            if(ch[fa][kind]==r){///左右交替
                Rotate(r,!kind);
                Rotate(r,kind);
            }
            else{               ///方向一致
                Rotate(fa,kind);
                Rotate(r,kind);
            }
        }
    }
    if(!goal) root=r;///goal为不存在结点时,r为变为根节点
}

bool Insert(int key){
    int r=root;
    while(ch[r][val[r]<key]){
        if(val[r]==key){///相同值只插入一次
            Splay(r,0);
            return false;
        }
        r=ch[r][val[r]<key];
    }
    newnode(ch[r][val[r]<key],r,key);
    Splay(ch[r][val[r]<key],0);
    return true;
}
///BST查询复杂度log(n)
int get_pre(int x){///寻找比X大但最接近X的数
    int t=ch[x][0];
    if(!t) return INF;
    while(ch[t][1]) t=ch[t][1];
    return val[x]-val[t];
}

int get_next(int x){///寻找比X小但最接近X的数
    int t=ch[x][1];
    if(!t) return INF;
    while(ch[t][0]) t=ch[t][0];
    return val[t]-val[x];
}

int main(){
    int n;
    //freopen("in.txt","r",stdin);
    while(~scanf("%d",&n)){
        root=tot=0;
        int ans=0;
        for(int i=1;i<=n;i++){
            int x;
            if(scanf("%d",&x)==EOF) x=0;
            if(i==1){
                ans+=x;
                newnode(root,0,x);
                continue;
            }
            if(!Insert(x)) continue;

            ans+=min(get_pre(root),get_next(root));
        }
        cout<<ans<<endl;
    }
    return 0;
}
时间: 2024-11-08 08:55:41

BZOJ 1588:splay tree的相关文章

BZOJ 1588:营业额统计(Splay)

http://www.lydsy.com/JudgeOnline/problem.php?id=1588 题意:中文题意. 思路:每一个点每一个点插入Splay,然后插入新的一个点之后,查这个节点的前驱和后继,即左子树最右边的点和右子树最左边的点.然后再从两个点中取个差值较小的就是答案了.要注意Rotate的时候一些细节(要给 rt 的父亲的父亲更新其孩子的值),还有Splay的细节:如果 rt 和 父节点都是要旋转相同方向,应该先旋转父亲节点再旋 rt,如果旋转不同方向就都是旋 rt. 1 #

bzoj 1588 平衡树 splay

1588: [HNOI2002]营业额统计 Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 15446  Solved: 6076[Submit][Status][Discuss] Description 营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额.分析营业情况是一项相当复杂的工作.由于节假日,大减价或者是其

BZOJ 1588 营业额统计 Splay

主要操作为Splay中插入节点,查找前驱和后继节点. 1: #include <cstdio> 2: #include <iostream> 3: #include <cmath> 4: using namespace std; 5: #define MaxL 100005 6: #define INF 0x7ffffff 7: #define keyTree sp[sp[root].child[1]].child[0] 8:   9: struct SplayTree

bzoj 3223/tyvj 1729 文艺平衡树 splay tree

原题链接:http://www.tyvj.cn/p/1729 这道题以前用c语言写的splay tree水过了.. 现在接触了c++重写一遍... 只涉及区间翻转,由于没有删除操作故不带垃圾回收,具体如下: 1 #include<cstdio> 2 #include<cstdlib> 3 #include<iostream> 4 #include<algorithm> 5 const int MAX_N = 100010; 6 struct Node{ 7

Splay Tree(伸展树)

参考:<数据结构(C++语言版)>邓俊辉著 (好书 一. 伸展树(由 D. D. Sleator 和 R. E. Tarjan 于 1985 年发明)也是平衡二叉搜索树的一种形式.相对于 AVL 树,伸展树的实现更为简洁 伸展树无需时刻都严格地保持全树的平衡,但却能够在任何足够长的真实操作序列中,保持分摊意义上的高效率 伸展树也不需要对基本的二叉树节点结构做任何附加的要求或改动,更不需要记录平衡因子或高度之类的额外信息,故适用范围更广 二.局部性 信息处理的典型模式是,将所有的数据项视作一个集

Splay Tree

伸展树 和AVL树不一样,伸展树并不保证每次操作的时间复杂度为O(logn),而保证任何一个m个操作的序列总时间为O(mlogn).伸展树的基本思想是:每个结点被访问时,使用AVL树的旋转操作把它移动到根.由于旋转是自底向上的,所以需要设置父亲指针,而不像AVL树那样以儿子为轴旋转.伸展操作(splaying) 伸展树的核心是伸展操作Splay(x,S).它是在保持伸展树有序性的前提下,通过一系列旋转将伸展树S中的元素x调整到树的根部.在调整的过程中,要根据x的位置分以下三种情况分别处理.情况一

Splay Tree的删除操作

Splay Tree的插入操作,搜索操作,和删除操作都实现了,那么就可以使用来解题了. 指针的删除操作的处理还是那么难的,很多坎需要避开. 同一个坎还是坑了我好多次,就是指针传递的问题,什么时候需要修改指针本身的值,就必须返回指针或者传递指针的指针,或者传递指针的的实参. 这里的删除操作就是需要改变传递到函数的指针本身的,所以我这里使用了返回指针操作. 还有删除树的问题,之前的代码没做删除操作,所以没问题,现在需要逐个节点删除,所以要小心不能把整个树都删除了. 至此, splay 树的功能差不多

Geeks Splay Tree Insert 树的插入操作

Splay树的插入操作,只需要处理好插入节点的孩子节点就可以了,最重要的是不要破坏了BST的基本规则. 因为高度并不是Splay树的首要因素,所以插入的时候也是使用splay操作,然后在根节点插入. 参考:http://www.geeksforgeeks.org/splay-tree-set-2-insert-delete/ 对比一下使用插入创建的树和手工创建数的区别,先序遍历的结果: #pragma once #include<stdio.h> #include <stdlib.h&g

树-伸展树(Splay Tree)

伸展树概念 伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.它由Daniel Sleator和Robert Tarjan创造. (01) 伸展树属于二叉查找树,即它具有和二叉查找树一样的性质:假设x为树中的任意一个结点,x节点包含关键字key,节点x的key值记为key[x].如果y是x的左子树中的一个结点,则key[y] <= key[x]:如果y是x的右子树的一个结点,则key[y] >= key[x]. (02) 除了拥有二叉查找树的性质