clj的treap

终于要开始学习各种复杂的数据结构了。。明天早晨写一下picture 的求面积版然后就暂时不写线段树了

把网上clj的treap代码抄过来学习学习。。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<map>
#include<functional>
#include<vector>
#include<queue>
using namespace std;
const long MAXN=100010;
struct node
{
    long key; // 随机数
    long value; // 砖块高度
    long size; // 左儿子数,右儿子数,加本身
    long long sum; // 左儿子的和,右儿子的和,加本身大小
    node*ch[2]; // 左儿子为0,右儿子为0
    void update()
    {
        size=ch[0]->size+ch[1]->size+1;
        sum=ch[0]->sum+ch[1]->sum+value;
    }
    node()
    {
        sum=size=0;
        key=INT_MAX;
    }
    node(long _value);
}*null=new node;
inline long bigRand()
{
    return (rand()*RAND_MAX)+rand(); // 随机数
}
node::node(long _value) // 创建一个高度为_value的节点
    {
        value=_value; 
        sum=value;
        size=1;
        ch[0]=ch[1]=null; // 左右儿子为空
        key=bigRand(); // 随机数
    }
struct treap
{
    node*root;
    treap()
    {
        root=null; // 根设为空
    }
    void rotate(node*&t,bool b) // 旋转
    {
        node*p = t->ch[b];
        t->ch[b] = p->ch[!b];
        p->ch[!b] = t;
        t->update();
        p->update();
        if (root==t)
            root=p;
        t=p;
    }
    void insert(node*&t,long _value) // 往树里添加一个点
    {
        if (t==null) // 如果此时树为空
        {
            t=new node(_value); // 建立一个节点
            return ;
        }
        bool dir=_value>t->value; // direction 方向
        insert(t->ch[dir],_value); // 左右儿子
        if (t->ch[dir]->key<t->key) rotate(t,dir); // 随机旋转
        else t->update(); // 更新
    }
    void erase(node*&t,long _value) // 去掉一个值为_value的点
    {
        if (t==null) return ; // 如果此时树为空,返回(我估计没什么用)
        if (t->value==_value) // 如果找到节点,把这个点旋转到叶子节点上
        {
            bool dir=t->ch[1]->key<t->ch[0]->key;
            if (t->ch[dir]==null) // 如果到根节点上了,删除
            {
                delete t;
                t=null;
                return ;
            }
            rotate(t,dir); // 否则旋转
            erase(t->ch[+dir],_value); // 此时t为当时的t的ch[dir]儿子,而现在指向的是t
            t->update(); // 更新
            return ;
        }
        bool dir=_value>t->value; // 当前节点不是需要的
        erase(t->ch[dir],_value); // 递归进子树
        t->update();
    }
    void calckth(long kth,long &x,long long &sum) // 中位数是第kth个,x返回中位数的值,sum返回小于中位数的和
    {
        sum=0; // for循环不断加左边的和
        for (node*t=root;;) // 开一个t的指针为根
        {
            long cnt=t->ch[0]->size; // 左儿子的大小
            if (kth>=cnt) // 如果需要大于等于左边的
                sum+=t->ch[0]->sum; // sum加上左边节点的和
            if (kth==cnt) // 如果正好
            {
                x=t->value; // x为中位数
                return ;
            }
            if (kth>cnt) // 如果需要的比左儿子的多
            {
                kth-=cnt+1; // 减去左儿子个数和根
                sum+=t->value; // sum加上根的值
                t=t->ch[1]; // 递归进右儿子
            }
            else t=t->ch[0]; // 否则递归进左儿子
        }
    }
    void insert(long x)
    {
        insert(root, x);
    }
    void erase(long _value)
    {
        erase(root, _value);
    }
    long size()
    {
        return root->size;
    }
    long long sum()
    {
        return root->sum;
    }
};
long nBrick, nNeed;
long h[MAXN];
void init()
{
    freopen("砖块.txt","r",stdin);
    scanf("%ld%ld", &nBrick, &nNeed);
    for (long i=0;i<nBrick;++i)
        scanf("%ld",&h[i]);
}
void work()
{
    treap*Treap=new treap;
    long long ans=LONG_LONG_MAX;
    for (long i=0;i<nBrick;++i)
    {
        Treap->insert(h[i]);
        if (i>=nNeed)
            Treap->erase(h[i-nNeed]);
        if (i+1>=nNeed)
        {
            long long leftsum;
            long mid;
            Treap->calckth(nNeed>>1,mid,leftsum);
            long long rightsum=Treap->sum()-leftsum-mid;
            long long leftsize=nNeed>>1;
            long long rightsize=nNeed-leftsize-1;
            long long curcost=(leftsize*mid-leftsum)+(rightsum-rightsize*mid);
            ans=min(ans,curcost);
        }
    }
    printf("%lld\n",ans);
}
int main()
{
    init();
    work();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值