wikioi 1396 伸展树(两个模板)

Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。

Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管理学上定义了一种最小波动值来衡量这种情况:

该天的最小波动值 = min{|该天以前某一天的营业额-该天营业额|}

当最小波动值越大时,就说明营业情况越不稳定。

而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助Tiger来计算这一个值。

第一天的最小波动值为第一天的营业额。

第一行为正整数n(n<=32767),表示该公司从成立一直到现在的天数,接下来的n行每行有一个正整数ai(ai<=1000000),表示第i天公司的营业额。

输出文件仅有一个正整数,即每天最小波动值之和,小于231

6

5

1

2

5

4

6

12

结果说明:5+|1-5|+|2-1|+|5-5|+|4-5|+|6-5|=5+4+1+0+1+1=12


思路:伸展树从下午开始看,以前看过了,觉得太麻烦了,然后数据结构老师讲过之后,我学明白了,考试都秒过,然后现在了才开始做题。因为在数据结构实现中,本人比较喜欢数组替代指针,所以尼玛找了好久的模板才找到这个让自己觉得很爽又很能理解的。其他都是指针来实现,代码又长,烦死了。

因为找了一下午加一晚上,实在找不到数组实现的伸展树了,就查了这个入门题的解题报告,终于让我发现了这个好代码,嘿嘿。

参考代码博客:http://blog.csdn.net/rowanhaoa/article/details/24436703

说明:伸展树是建立在排序二叉树上的,所以左子树所有的值都比根小,根又都比右子树所有的值小。因为伸展树是平衡二叉树,所以具备平衡二叉树的特点,所有功能的平摊时间复杂度都是O(log n),所以在数据结构中用得非常爽,代码又是可以接受的复杂度和长度。而且每个结点也可以是区间,可以代替线段树,你说爽不爽。伸展树最大的优点是分裂和合并都是log n,所以比别的当然很快,在处理很多问题上自然有很多优点。从此,爱上伸展树……


模板一:

这个伸展树的结点是用数据写的,是参照某个博客的,比较方便,不过有时候有点慢,因为是数组,所以debug不出来的几率比较高。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<bitset>
#define mem(a,b) memset(a,b,sizeof(a))
#define INF 1000000007
#define maxn 100010
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
struct splaytree //伸展树
{
    int pre[maxn];//前驱
    int key[maxn];//键值
    int ch[maxn][2];//左右孩子,0为左孩子,1为右孩子
    int root,tot;//根节点,节点数量
    splaytree()
    {
        tot=root=0;
        mem(ch,0);
        mem(pre,0);
        mem(key,0);
    }
    void newnode(int &r,int father,int k)
    {//在r位置,父亲为father,新建一个值点。
        r=++tot;
        pre[r]=father;
        key[r]=k;
        ch[r][0]=ch[r][1]=0;
    }
    void rotate(int x,int kind)//kind=1表示右旋,kind=0表示左旋
    {
        int y=pre[x];
        ch[y][!kind]=ch[x][kind];
        pre[ch[x][kind]]=y;
        ch[x][kind]=y;
        if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x;
        pre[x]=pre[y];
        pre[y]=x;
    }
    void splay(int x,int goal) //把x伸展到目标位置
    {
        while(pre[x]!=goal)
        {
            if(pre[pre[x]]==goal) rotate(x,ch[pre[x]][0]==x);
            else
            {
                int y=pre[x];
                int kind=ch[pre[y]][0]==y;
                if(ch[y][kind]==x) rotate(x,!kind),rotate(x,kind);
                else rotate(y,kind),rotate(x,kind);
            }
        }
        if(!goal) root=x;//把root更新成x
    }
    int insert(int x)//插入值x
    {
        int r=root;
        while(ch[r][key[r]<x])
        {
            if(key[r]==x) {splay(r,0);return 0;}
            r=ch[r][key[r]<x];
        }
        newnode(ch[r][x>key[r]],r,x);
        splay(ch[r][x>key[r]],0);
        return 1;
    }
    int find(int x)//查找键值为x的结点编号
    {
        int r=root;
        if(!r) return -INF;//树未建,未找到
        if(key[r]==x) return r;
        while(ch[r][x>key[r]])
        {
            r=ch[r][x>key[r]];
            if(key[r]==x) return r;
        }
        return -INF;//未找到
    }
    int get_pre(int x)//寻找节点x的前驱的值,未找到则返回INF
    {
        int r=ch[x][0];
        if(!r) return -INF;
        while(ch[r][1]) r=ch[r][1];
        return key[r];
    }
    int get_next(int x)
    {
        int r=ch[x][1];
        if(!r) return INF;
        while(ch[r][0]) r=ch[r][0];
        return key[r];
    }
};
int main()
{
    int sum=0,a,n;
    splaytree tree;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a);
        if(!i)
        {
            sum+=a;
            tree.newnode(tree.root,0,a);
            continue;
        }
        if(!tree.insert(a)) continue;
        //因为既然前面已经有a,那这天的相减为0,所以下面就不用继续了
        int x=tree.get_pre(tree.root);//因为insert之后根root即为当前a的
        int y=tree.get_next(tree.root);//故可从根查找
        sum+=min(a-x,y-a);
    }
    cout<<sum<<endl;
    return 0;
}

模板二:结构体结点

这个是参照百度百科上的代码写的,刚开始写的时候运行这题的数据出错,然后debug了好久,还以为代码中的splay和旋转错了,都已经放弃然后去找别的模板了。但是后面还是输出其中过程的数据才知道错哪了。因为和数组的模板不一样,所以是自己传参的时候传错了,本来是传结点编号的,我传成值了。

为什么上面的模板不用,又找了这个呢。是因为我用上面的模板写一道题的时候严重超时,然后改又改不了了,然后自己又找了这个模板,比较好理解。数组类的模板写法太飘逸了,咱等平常人一时难以接受,自己写的删除结点函数又不太会,所以只能暂时放弃数组的模板了。用下面这个比较好理解,又好写,虽然代码是长了点。

这个模板不足之处就是insert的时候没有判断是否已经存在本值,size就直接加了。这个在有些题面前改的东西就多了,上面数组模板就有优势了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<bitset>
#define mem(a,b) memset(a,b,Sizeof(a))
#define INF 1000000007
#define maxn 100010
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
struct splayNode
{
    int l, r, fa, val, size;
};
struct splaytree
{
    splayNode t[maxn];
    int root, tot;
    void create()
    {
        root = 1, tot = 2;
        t[1].val = -INF;
        t[2].val = INF;
        t[1].r = t[1].size = 2;
        t[2].fa = t[2].size = 1;
    }
    void dfs(int x)
    {
        if(x)
        {
            dfs(t[x].l);
            printf("结点%2d: 左儿子 %2d 右儿子 %2d 父结点 %2d 键值为:%2d\n",x,t[x].l,t[x].r,t[x].fa,t[x].val);
            dfs(t[x].r);
        }
    }
    void debug()
    {
        printf("根为:%d\n",root);
        dfs(root);
    }
    void update(int x)
    {
        t[x].size=t[t[x].l].size+t[t[x].r].size+1;
    }
    void left(int x)
    {
        int fa = t[x].fa;
        t[x].fa = t[fa].fa;
        if (t[t[fa].fa].l==fa) t[t[fa].fa].l=x;
        if (t[t[fa].fa].r==fa) t[t[fa].fa].r=x;
        t[fa].fa = x;
        t[fa].r = t[x].l;
        t[t[x].l].fa = fa;
        t[x].l = fa;
        update(fa);
    }
    void right(int x)
    {
        int fa = t[x].fa;
        t[x].fa = t[fa].fa;
        if (t[t[fa].fa].l==fa) t[t[fa].fa].l=x;
        if (t[t[fa].fa].r==fa) t[t[fa].fa].r=x;
        t[fa].fa = x;
        t[fa].l = t[x].r;
        t[t[x].r].fa = fa;
        t[x].r = fa;
        update(fa);
    }
    void splay(int x, int FA = 0)
    {
        while (t[x].fa != FA)
        {
            int fa = t[x].fa;
            if (t[fa].fa == FA)
            {
                if (t[fa].l==x) right(x);
                else left(x);
            }
            else if (t[t[fa].fa].l==fa)
            {
                if(t[fa].l==x) right(fa),right(x);
                else left(x), right(x);
            }
            else if (t[fa].l==x) right(x),left(x);
            else left(fa), left(x);
        }
        update(x);
        if (!FA) root = x;
    }
    int lower_bound(int val)
    {
        int ans = 0, la = 0;
        for (int x = root; x;)
        {
            la = x;
            if (t[x].val>=val) ans=x,x=t[x].l;
            else x = t[x].r;
        }
        splay(la);
        return ans;
    }
    void insert(int val)
    {
        for (int x = root;;)
        {
            ++t[x].size;
            if (t[x].val >= val)
            {
                if (t[x].l) x = t[x].l;
                else
                {
                    t[x].l = ++tot;
                    t[tot].size = 1;
                    t[tot].fa = x;
                    t[tot].val = val;
                    splay(tot);
                    return;
                }
            }
            else if (t[x].r) x = t[x].r;
            else
            {
                t[x].r = ++tot;
                t[tot].size = 1;
                t[tot].fa = x;
                t[tot].val = val;
                splay(tot);
                return;
            }
        }
    }
    int get_lower(int a)
    {
        splay(a);
        for (a = t[a].l; t[a].r; a = t[a].r);
        return t[a].val;
    }
    int get_upper(int a)
    {
        splay(a);
        for (a = t[a].r; t[a].l; a = t[a].l);
        return t[a].val;
    }
    int get_rank(int a)
    {
        splay(a);
        return t[t[a].l].size;
    }
    void del(int l, int r)
    {
        l = get_lower(l);
        r = get_upper(r);
        splay(l);
        splay(r, l);
        t[r].l = 0;
        update(r);
        update(l);
    }
    int get_kth(int k)
    {
        ++k;
        for (int x=root;;)
        {
            if (t[t[x].l].size==k-1)
                return x;
            if (t[t[x].l].size>=k) x=t[x].l;
            else k-=t[t[x].l].size+1,x=t[x].r;
        }
    }
}tree;
map<int,int>Map;
int main()
{
    int n,a,sum=0;
    tree.create();
    cin>>n;
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a);
        //tree.debug();
        if(!i) sum+=a,tree.insert(a),Map[a]=1;
        else
        {
            if(Map[a]) continue;
            Map[a]=1;
            tree.insert(a);
            //tree.debug();
            int x=tree.get_lower(tree.root);
            int y=tree.get_upper(tree.root);
            //cout<<"+++"<<x<<' '<<y<<endl;
            sum+=min(a-x,y-a);
        }
    }
    cout<<sum<<endl;
    return 0;
}



你可能感兴趣的:(wikioi 1396 伸展树(两个模板))