HYSBZ - 1588 Treap

1588: [HNOI2002]营业额统计

Time Limit: 5 Sec  Memory Limit: 162 MB
Submit: 19665  Solved: 8365
[Submit][Status][Discuss]

Description

营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。 Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管理学上定义了一种最小波动值来衡量这种情况: 该天的最小波动值 当最小波动值越大时,就说明营业情况越不稳定。 而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助Tiger来计算这一个值。 第一天的最小波动值为第一天的营业额。  输入输出要求

Input

第一行为正整数 ,表示该公司从成立一直到现在的天数,接下来的n行每行有一个整数(有可能有负数) ,表示第i

天公司的营业额。

天数n<=32767,

每天的营业额ai <= 1,000,000。

最后结果T<=2^31

 

Output

输出文件仅有一个正整数,即Sigma(每天最小的波动值) 。结果小于2^31 。

Sample Input

6
5
1
2
5
4
6

Sample Output

12

HINT

 

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

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
#define enter printf("\n")
#define space printf(" ")
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
inline ll read()
{
    ll ans = 0;
    char ch = getchar(), last = ' ';
    while(!isdigit(ch)) {last = ch; ch = getchar();}
    while(isdigit(ch))
    {
        ans = ans * 10 + ch - '0'; ch = getchar();
    }
    if(last == '-') ans = -ans;
    return ans;
}
inline void write(ll x)
{
    if(x < 0) x = -x, putchar('-');
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}


int n, root = 0; //n种操作;root记录根节点是谁(因为进行某一操作后,根节点可能改变,所以要随时记录)
int cnt = 0, lson[maxn], rson[maxn]; //cnt:节点总数(即每一个节点的编号);lson[now],rson[now]:节点now的左右孩子
int val[maxn], ran[maxn], size[maxn], Cnt[maxn];//val[now]:节点now的权值;ran[now]:随机出来的优先级;size[now]:子树大小;
//Cnt[now]记录和val[now]相同的节点多少个(用来处理数字重复)
void update(int now)
{
    if(!now) return;
    size[now] = size[lson[now]] + size[rson[now]] + Cnt[now];
}
void right_rotate(int& Q)
{
    int P = lson[Q];
    lson[Q] = rson[P];            //这个和下面那句不能反
    rson[P] = Q;
    update(Q); update(P);
    Q = P;
}
void left_rotate(int& Q)
{
    int P = rson[Q];
    rson[Q] = lson[P];
    lson[P] = Q;
    update(Q); update(P);
    Q = P;
}
void insert(int& now, int v)//插入v
{
    if(!now)                //找到要插入的叶节点了
    {
        now = ++cnt;        //新建节点
        val[now] = v;
        size[now] = Cnt[now] = 1;
        ran[now] = rand();    //随机优先级
        return;
    }
    if(val[now] == v) Cnt[now]++;    //若树中已经有了该数,就直接Cnt[]++了
    else if(val[now] > v)        //说明在左子树
    {
        insert(lson[now], v);    //递归寻找
        if(ran[lson[now]] < ran[now]) right_rotate(now);
        //这一步放在了递归后面,说明此时节点已经插入好了(而且只是修改了左子树),那就判断并通过旋转维护堆
    }
    else
    {
        insert(rson[now], v);
        if(ran[rson[now]] < ran[now]) left_rotate(now);
    }
    update(now);
}
void del(int& now, int v)//删除v
{
    if(!now) return;
    if(val[now] == v)    //找到了该数
    {
        if(Cnt[now] > 1) //有重复
        {
            Cnt[now]--;
            update(now); return;
        }
        else if(lson[now] && rson[now])    //并没有旋转到根节点
        {
            left_rotate(now);     //只要选任意一棵子树旋转就行
            del(lson[now], v);    //这两句等价于right_rotate(now); del(rson[now], v);
        }
        else                //代表只剩一个孩子了,那么就直接用他的孩子代替他,相当于把他删除
        {
            now = lson[now] | rson[now];    //等价于now = lson[now] ? lson[now] : rson[now]
            update(now); return;
        }
    }
    else if(val[now] > v) del(lson[now], v);        //没找到就接着找
    else del(rson[now], v);
    update(now);
}
int Find_id(int now, int v)//查询id排名
{
    if(!now) return 0;
    if(val[now] == v) return size[lson[now]] + 1;        //别忘加上自己
    if(val[now] > v) return Find_id(lson[now], v);
    else return Find_id(rson[now], v) + size[lson[now]] + Cnt[now];
}
int Find_num(int now, int id)//查询排名为id的数
{
    if(!now) return INF;
    if(size[lson[now]] >= id) return Find_num(lson[now], id);        //在左子树
    else if(id <= size[lson[now]] + Cnt[now]) return val[now];        //在左子树和自己,但因为左子树的已经走上面的语句了,就指自己
    else return Find_num(rson[now], id - size[lson[now]] - Cnt[now]);    //右子树,别忘减去(跟线段树找第k小挺像)
}
int Pre(int now, int v)//小于等于
{
    if(!now) return -INF;
    if(val[now] <= v) return max(val[now], Pre(rson[now], v));//前驱在右子树或是当前节点
    else return Pre(lson[now], v);
}
int Nex(int now, int v)//大于等于
{
    if(!now) return INF;
    if(val[now] >= v) return min(val[now], Nex(lson[now], v));//去掉前面的等号就变成大于了
    else return Nex(rson[now], v);
}

int main()
{
    n = read();
     int x=read(),sum=x;
    insert(root,x);
    while(--n)
    {
        x=read();
        sum+=min(x-Pre(root,x),Nex(root,x)-x);
        //cout<<Pre(root,x)<<endl;
        insert(root,x);
        //int d = read(), x = read();


        /*if(d == 1) insert(root, x);
        else if(d == 2) del(root, x);
        else if(d == 3) {write(Find_id(root, x)); enter;}
        else if(d == 4) {write(); enter;}
        else if(d == 5) {write(Pre(root, x)); enter;}
        else {write(Nex(root, x)); enter;}*/
    }
    printf("%d\n",sum);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Splay Tree和Treap是两种常用的自平衡二叉查找树,它们在数据结构和算法中都有应用。它们的区别主要体现在平衡策略和随机性的使用上: 1. **平衡策略**: - **Splay Tree**: Splay Tree采用的是"最近最频繁访问"(Most Recently Used, MRU)的原则。每当查找、插入或删除一个元素后,会调整树使其根节点指向这个操作的直接结果,从而使得最近频繁访问的节点更接近根。这种操作是动态的,不需要预先知道访问模式。 - **Treap**: Treap是一种结合了二叉查找树和堆的数据结构,它的平衡是基于随机优先级。每个节点除了有一个键值用于排序外,还有一个随机优先级。这样保证了在平均情况下,查找、插入和删除的时间复杂度接近于二叉查找树(O(log n)),而随机性确保了在某些特定情况下能较快地达到平衡。 2. **插入和删除性能**: - Splay Tree插入和删除后,由于其动态调整,通常能保持局部平衡,但全局平衡可能不是最优的。 - Treap的平衡依赖于随机优先级,虽然不能保证每次操作后的全局平衡,但期望的平均性能良好。 3. **内存消耗**: - Treap因为包含随机优先级,需要额外存储优先级信息,这可能会稍微增加内存消耗。 4. **实现复杂性**: - Splay Tree的实现相对简单,因为其调整规则明确,但需要对旋转操作有深入理解。 - Treap的实现稍微复杂一些,因为它需要维护两个属性(键值和优先级),并且在插入和删除时需要处理随机优先级的更新。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值