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()
}
}
简化题意: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;
}
}
}