BZOJ 1588 营业额统计 (Splay Tree)

来源:互联网 发布:豆瓣淘宝卖家讨论区 编辑:程序博客网 时间:2024/04/30 06:38

题目大意:

中文题面= =


大致思路:

作为初学Splay的第一道练习题.....

使用到的操作:

1.在原序列末尾插入新元素, 键值不出现重复

2. 寻找比某个键值小或者大的最近的键值, 通过Splay转为求根节点的前驱和后继


代码如下:

Result  :  Accepted     Memory  :  2836 KB     Time  :  140 ms

/* * Author: Gatevin * Created Time:  2015/8/21 15:30:30 * File Name: Sakura_Chiyo.cpp */#include<iostream>#include<sstream>#include<fstream>#include<vector>#include<list>#include<deque>#include<queue>#include<stack>#include<map>#include<set>#include<bitset>#include<algorithm>#include<cstdio>#include<cstdlib>#include<cstring>#include<cctype>#include<cmath>#include<ctime>#include<iomanip>using namespace std;const double eps(1e-8);typedef long long lint;struct Splay_Tree{#define maxn 100010    int pre[maxn];//u父亲节点是pre[u]    int key[maxn];//键值    int next[maxn][2];//next[u][0]是结点u的左子树, next[u][1]是u的右子树    int root;//当前的根节点    int tot;//总的结点数量    void newnode(int &r, int father, int value)//新建结点, 父亲是father, 键值为value    {        r = ++tot;        pre[r] = father;        key[r] = value;        next[r][0] = next[r][1] = 0;//新节点r的左右子树为空        return;    }        //旋转, kind = 1表示右旋, kind = 0表示左旋    void Rotate(int x, int kind)    {        int y = pre[x];        next[y][!kind] = next[x][kind];        pre[next[x][kind]] = y;        if(pre[y])            next[pre[y]][next[pre[y]][1] == y] = x;        pre[x] = pre[y];        next[x][kind] = y;        pre[y] = x;        return;    }        //Splay伸展调整, 将根为r的子树调整为goal, (将r所在子树调整为goal的左或者右子树    void Splay(int r, int goal)//goal为0会旋转至根节点, pre[root] = 0    {        //旋转会使得r的父亲是goal        while(pre[r] != goal)        {            if(pre[pre[r]] == goal)//只需要进行一次Zig或者Zag操作即可                Rotate(r, next[pre[r]][0] == r);//r是左子树就右旋, 否则左旋            else            {                int y = pre[r];                int kind = next[pre[y]][0] == y;//y是否是左子树                                if(next[y][kind] == r)//y左r右或者 y右r做, 说明要进行Zig-Zag或者Zag-Zig操作                {                    Rotate(r, !kind);                    Rotate(r, kind);                }                else//y, r都是左子树或者都是右子树, 进行Zig-Zig或者Zag-Zag操作                {                    Rotate(y, kind);                    Rotate(r, kind);                }            }        }        if(goal == 0) root = r;    }        int insert(int k)//插入一个键值k    {        int r = root;        while(next[r][key[r] < k])        {            if(key[r] == k)//重复元素, 不插入            {                Splay(r, 0);//将r旋转至根节点                return 0;//插入失败            }            r = next[r][key[r] < k];        }        newnode(next[r][k > key[r]], r, k);        Splay(next[r][k > key[r]], 0);//将新插入的结点旋转至根        return 1;//插入成功    }        //寻找结点编号为x的结点的前驱结点, 即左子树的最右结点    int getPre(int x)//返回结点编号, -1表示没有    {        int tmp = next[x][0];        if(tmp == 0) return -1;        while(next[tmp][1])            tmp = next[tmp][1];        return tmp;//    }        //寻找结点编号为x的结点的后继结点, 即右子树的最左结点    int getNext(int x)//返回结点编号, 编号为-1表示没有    {        int tmp = next[x][1];        if(tmp == 0) return -1;        while(next[tmp][0])            tmp = next[tmp][0];        return tmp;    }};Splay_Tree tree;int n;const int inf = 1e9 + 7;int main(){    while(~scanf("%d", &n))    {        tree.root = tree.tot = 0;        int ans = 0;        for(int i = 1; i <= n; i++)        {            int num;            if(scanf("%d", &num) == EOF) num = 0;//莫名的奇怪判断...            if(i == 1)            {                ans += num;                tree.newnode(tree.root, 0, num);                continue;            }            if(tree.insert(num) == 0) continue;//重复值            //由于进行了insert操作num一定在根的位置            int tmp = inf;            int a = tree.getPre(tree.root);            int b = tree.getNext(tree.root);            if(a != -1) tmp = min(tmp, tree.key[tree.root] - tree.key[a]);            if(b != -1) tmp = min(tmp, tree.key[b] - tree.key[tree.root]);            ans += tmp;        }        printf("%d\n", ans);    }    return 0;}


0 0
原创粉丝点击