Treap

Treap

BST 二叉查找树

对于每个节点p,左子树都比p小,右子树都比p大

insert

插入一个节点p

过程:从root开始,比当前根节点小 去左子树,否则去右子树,不停往下找直到找到一个空的位置然后p变成叶子节点

delete

删除一个节点p

若p为叶子节点直接删除

若p有一个儿子则将其连接到p的父亲上,删除p

否则从p的右儿子开始,沿着左儿子一直向下到叶子节点,此时找到的是第一个比p大的点,可以让这个节点继承p的所有信息,然后删除p

查询

Heap 堆

完全二叉树,根节点比子树都小/大,用数组存,下面是手写堆

struct heap_max {
    int size, d[N];
    int top { return d[1]; }
    void clear { size = 0; memset(d, 0, sizeof d); }
    
    void push(int x) {
        d[++ size] = x;
        int p = size, flag = 1;
        while(flag and (p > 1)) {
            if(d[p >> 1] < d[p]) swap(d[p >> 1], d[p]);
            else flag = 0;
            p >>= 2;
        }
    }
    
    void pop() {
        swap(d[1], d[size]);
        size --;
        int p = 1, flag = 1, t;
        while(flag and (p << 1 <= size)) {
            if(d[p << 1] > d[p]) t = p << 1; else t = p;
            if(p << 1 < size and (d[(p << 1) + 1] > d[p]) and (d[(p << 1) + 1] > d[p << 1])) 
                t = (p << 1) + 1;
            if(t != p) swap(d[p], d[t]), p = t; else flag = 0;
        }
    }
}

Treap

维护的信息

\(rt\) \(b\) \(rd[i]\) \(o[i][0/1]\) \(v[i]\) \(u[i]\) \(sz[i]\)
根节点编号 节点个数 随机值 左/右儿子编号 节点权值 权值出现次数 子树大小

关于rotate的图

​ A 左旋 C

​ / \ ------------> / \

B C <------------ A E

​ / \ 右旋 / \

​ D E B D

#include<bits/stdc++.h>
#define M 100010
#define inf 2000000005
using namespace std;
int rt, b, sz[N], v[N], u[N], rd[N], o[N][2];

void mtn(int p) { sz[p] = sz[o[p][0]] + sz[o[p][1]] + u[p]; }
//更新子树大小

void rotate(int &p, int d) { //d=0为例
    int oo = o[p][d ^ 1]; //oo存节点p的右儿子(可以画一下)
    o[p][d ^ 1] = o[oo][d]; //oo = oo的左儿子
    o[oo][d] = p; //oo的左儿子 = p
    mtn(p), mtn(oo); //及时更新
    p = oo; //换根
}//d=0左旋, d=1右旋

void ins(int &p, int x) {
    if(!p) { //当前节点为空
        p = ++ b; //总节点数 ++
        sz[p] = u[p] = 1; //新建信息,初始为1
        v[p] = x; //权值大小
        rd[p] = rand(); //用来维护堆的随机值
        return;
    }
    if(v[p] == x) { //当前节点与要插入的节点权值相同,存一起
        u[p] ++, sz[p] ++; //权值出现次数 ++, 子树大小 ++
        return;
    }
    ins(o[p][x > v[p]], x); //要插入的权值比当前节点大则为1,去右子树,否则去左子树
    if(rd[p] < rd[o[p][f]]) rotate(p, f ^ 1); //insert完成,若随机值<右儿子,左旋
    mtn(p);
}

void del(int &p, int x) {
    if(!p) return; //访问到空节点(要删的数不存在)
    if(x == v[p]) { //如果找到这个节点了
        if(!o[p][0] and !o[p][1]) { //这个节点为叶子节点
            u[p] --, sz --; //直接减1
            if(u[p] == 0) p = 0; //没有了就删掉
        } else 
        if(o[p][0] and !o[p][1]) { //有一个儿子,把子节点旋上来先,再去相应子树删
            rotate(p, 1);
            del(o[p][1], x);
        } else
        if(!o[p][0] and o[p][1]) { //同上
            rotate(p, 0);
            del(o[p][0], x);
        } else 
        if(o[p][0] and o[p][1]) { //若有两个儿子,把rand值较大的旋上来,然后去另一个子树解决
            rotate(p, (rd[o[p][0]] > rd[o[p][1]]));
            del(o[p][rd[o[p][0]] > rd[o[p][1]]], x);
        }
    } else del(o[p][x > v[p]], x); //x > 当前节点 则去右子树,否则去左子树
}

int rk(int p, int x) { //查询x在根为p的树中的排名
    if(!p) return 0; //访问到空节点,返回
    if(v[p] == x) return sz[o[p][0]] + 1; //若与当前节点一样,左子树大小 + 1
    if(v[p] < x) return sz[o[p][0]] + u[p] + rk(o[p][1], x); //左子树的大小+当前节点的大小+在右子树的排名
    if(v[p] > x) return rk(o[p][0], x); //比较小,去左子树寻找
}

int kth(int p, int x) { //查询在根为p中排名为x的数
    if(!p) return 0; //不存在,返回0
    if(sz[o[p][0]] >= x) return kth(o[p][0], x); //左子树大小比x大,沿着左子树向下找 
    else if(sz[o[p][0]] + u[p] < x) return kth(o[p][0], x - u[p] - sz[o[p][0]]);
    //在右子树里接着找,去掉左子树的大小和当前节点的大小->在右子树的排名
    else return v[p]; //排除以上两种情况,当前节点就是
}

int pre(int p, int x) { //根为p的子树中x的前驱
    if(!p) return -inf; //空,無前驱
    if(v[p] >= x) return pre(o[p][0], x); //沿着左子树向下找
	else return max(v[p], pre(o[p][1], x)); //有可能是根或者在右子树,在小于x的数里选最大值
}

int nxt(int p, int x) { //根为p的子树中x的后继
    if(!p) return -inf; //空,無后继
    if(v[p] <= x) return nxt(o[p][1], x); //沿着右子树向下找
    else return min(v[p], nxt(o[p][0], x)); //有可能是根或在左子树,在大于x的数里选最小值
}

int main() {
    cin >> n;
    for(int i = 1, op, x; i <= n; i ++) {
        cin >> op >> x;
        if(op == 1) del()
    }
}

1691.[Usaco2007 Dec]挑剔的美食家

简化题意:Farmer John有 N 头牛,第 i 头牛要求午饭吃的牧草 价格不低于 \(A_i\),鲜嫩程度不低于 \(B_i\) ,商店供应 M 种不同的牧草,第 i 种价格为 \(C_i\) ,鲜嫩程度为 \(D_i\) ,且一种牧草只能给一头牛,在满足条件的情况下,最少花多少钱

数据范围\(1\le N,M\le100~000~~~~~~~~~~1\le A_i,B_i,C_i,D_i\le1000~000~000\)

#include<bits/stdc++.h>
using namespace std;

struct hh { int a, b; } o[N], c[N];
struct ee { int l, r, v, s, rand, w; } t[N];
int n, m, size, root, ans;
void update(int p) { t[p].s = t[t[p].l].s + t[t[p].l].s + t[p].w; }
void re(int &p) { 
    int tr = t[p].l; //p的左儿子 
    t[p].l = t[p].r; //右儿子变成左儿子
    t[tr].r = p; //p原来左儿子的右儿子变成p
    t[tr].s = t[p].s; //p原来左儿子的大小变为p的大小
    update(p); //更新p的大小
    p = tr; 
}

int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i ++) cin >> o[i].a >> o[i].b;
    for(int i = 1; i <= m; i ++) cin >> c[i].a >> c[i].b;
    sort(o + 1, o + n + 1, cmp);
    sort(c + 1, c + m + 1, cmp);
    for(int i = 1; i <= n; i ++) {
        ans = -1;
        while(c[j].b >= b[j].a) { insert(root, b[j].a); j ++; }
        query(root, o[i].a);
        if(ans == -1) { cout << -1; return 0; }
        res += ans; 
        del(root, ans);
    }
    cout << ans;
    return 0;
}
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+10;
struct data {
    int l,r,v,size,rnd,w;
}tr[N];
int n,size,root,ans;
void update(int k)
{
    tr[k].size=tr[tr[k].l].size+tr[tr[k].r].size+tr[k].w;
}
void rturn(int &k)
{
    int t=tr[k].l;tr[k].l=tr[t].r;tr[t].r=k;
    tr[t].size=tr[k].size;update(k);k=t;
}
void lturn(int &k)
{
    int t=tr[k].r;tr[k].r=tr[t].l;tr[t].l=k;
    tr[t].size=tr[k].size;update(k);k=t;
}
void insert(int &k,int x)
{
    if(k==0) {
        size++;k=size;
        tr[k].size=tr[k].w=1;tr[k].v=x;tr[k].rnd=rand();
        return ;
    }
    tr[k].size++;
    if(tr[k].v==x) tr[k].w++;
    else if(x>tr[k].v) {
        insert(tr[k].r,x);
        if(tr[tr[k].r].rnd<tr[k].rnd) lturn(k);
    }else {
        insert(tr[k].l,x);
        if(tr[tr[k].l].rnd<tr[k].rnd) rturn(k);
    }   
}
void del(int &k,int x)
{
    if(k==0) return ;
    if(tr[k].v==x) {
        if(tr[k].w>1) {
            tr[k].w--;tr[k].size-- ;return ;
        }
        if(tr[k].l*tr[k].r==0) k=tr[k].l+tr[k].r;
        else if(tr[tr[k].l].rnd<tr[tr[k].r].rnd) 
            rturn(k),del(k,x);
        else lturn(k),del(k,x);
    }
    else if(x>tr[k].v) 
        tr[k].size--,del(tr[k].r,x);
    else tr[k].size--,del(tr[k].l,x);
}
int query_rank(int k,int x)
{
    if(k==0) return 0;
    if(tr[k].v==x) return tr[tr[k].l].size+1;
    else if(x>tr[k].v) 
        return tr[tr[k].l].size+tr[k].w+query_rank(tr[k].r,x);
        else return query_rank(tr[k].l,x);
}
int query_num(int k,int x)
{
    if(k==0) return 0;
    if(x<=tr[tr[k].l].size) 
        return query_num(tr[k].l,x);
    else if(x>tr[tr[k].l].size+tr[k].w) 
        return query_num(tr[k].r,x-tr[tr[k].l].size-tr[k].w);
    else return tr[k].v;
}
void query_pro(int k,int x)
{
    if(k==0) return ;
    if(tr[k].v<x) ans=k,query_pro(tr[k].r,x);
    else query_pro(tr[k].l,x);
}
void query_sub(int k,int x)
{
    if(k==0) return ;
    if(tr[k].v>x) ans=k,query_sub(tr[k].l,x);
    else query_sub(tr[k].r,x); 
}
int main()
{
    cin >> n;
    for(int i = 1, x, op; i <= n; i ++) {
        scanf("%d%d",&op,&x);   
        switch(op){
        case 1: insert(root, x); break;
        case 2: del(root, x); break;
        case 3: printf("%d\n",query_rank(root, x)); break;
        case 4: printf("%d\n",query_num(root, x)); break;
        case 5: ans = 0; query_pro(root,x); printf("%d\n", tr[ans].v); break;
        case 6: ans = 0; query_sub(root,x); printf("%d\n", tr[ans].v); break;
        }
    }
}
posted @ 2020-08-25 21:23  hulne  阅读(1)  评论(0编辑  收藏  举报