『学习笔记』平衡树(todo)
会一种即可。
比线段树多的功能:
- 区间翻转
- 插入/删除
\(\large\texttt{Treap}\)
Treap 即 Tree + Heap。相比二叉搜索树,每个节点多维护一个随机数 \(pri\) 作为权值,并按照 \(pri\) 维护整棵树的堆的性质。这样可以避免复杂度退化。
板子:
const int N=1e5+5,inf=0x3f3f3f3f;
class Treap{
public:
void insert(int &rt,int x){
if(!rt){
rt=++cnt;
t[rt].size=t[rt].cnt=1;
t[rt].val=x;
t[rt].pri=rand();
return;
}
if(t[rt].val==x){
t[rt].cnt++,t[rt].size++;
return;
}
int k=x>t[rt].val;
insert(t[rt].s[k],x);
if(t[rt].pri<t[t[rt].s[k]].pri) rotate(rt,k^1);
else pushup(rt);
}
void remove(int &rt,int x){
if(!rt) return;
if(x<t[rt].val) remove(t[rt].s[0],x);
if(x>t[rt].val) remove(t[rt].s[1],x);
if(x==t[rt].val){
if(!t[rt].s[0] && t[rt].s[1]) rotate(rt,0),remove(t[rt].s[0],x);
else if(t[rt].s[0] && !t[rt].s[1]) rotate(rt,1),remove(t[rt].s[1],x);
else if(t[rt].s[0] && t[rt].s[1]){
int k=t[t[rt].s[0]].pri>t[t[rt].s[1]].pri;
rotate(rt,k),remove(t[rt].s[k],x);
}else{
t[rt].cnt--,t[rt].size--;
if(!t[rt].cnt) rt=0;
}
}
pushup(rt);
}
int rank(int rt,int x){
if(!rt) return 1;
if(x<t[rt].val) return rank(t[rt].s[0],x);
if(x>t[rt].val) return t[t[rt].s[0]].size+t[rt].cnt+rank(t[rt].s[1],x);
return t[t[rt].s[0]].size+1;
}
int kth(int rt,int x){
if(!rt) return 0;
if(x<=t[t[rt].s[0]].size) return kth(t[rt].s[0],x);
if(x>t[t[rt].s[0]].size+t[rt].cnt) return kth(t[rt].s[1],x-t[t[rt].s[0]].size-t[rt].cnt);
return t[rt].val;
}
int pre(int rt,int x){
if(!rt) return -inf;
if(x<=t[rt].val) return pre(t[rt].s[0],x);
return max(t[rt].val,pre(t[rt].s[1],x));
}
int suc(int rt,int x){
if(!rt) return inf;
if(x>=t[rt].val) return suc(t[rt].s[1],x);
return min(t[rt].val,suc(t[rt].s[0],x));
}
private:
struct node{
int s[2];
int val;
int size,cnt,pri;
}t[N];
int cnt;
inline void pushup(int rt){t[rt].size=t[t[rt].s[0]].size+t[t[rt].s[1]].size+t[rt].cnt;}
inline void rotate(int &rt,int x){ // x=0 左 x=1 右
int k=t[rt].s[x^1];
t[rt].s[x^1]=t[k].s[x];
t[k].s[x]=rt;
pushup(rt),pushup(k);
rt=k;
}
};
\(\large\texttt{FHQ Treap}\)
P4036 [JSOI2008] 火星人
需要插入,那么平衡树维护 hash,二分答案。
P3215 [HNOI2011] 括号修复 / [JSOI2011] 括号序列
给定括号序列,需要支持区间修改、翻转、反转,询问至少修改多少位才能使括号序列合法。
设左右括号分别为 \(-1\) 和 \(1\),对于询问可以维护前缀最大值与后缀最小值。
对于翻转操作,我们同时需要维护翻转前后的信息。注意打标记的细节。
对于反转操作,同样可以维护两份信息。每个结点一共要维护四个信息。
FHQ Treap 即可。
启发:先看询问,设计节点信息,再考虑修改标记。
CF702F T-Shirts
首先对人和物品都排序,对人建立平衡树,维护钱数和买的衣服数。对于每个物品,将钱数够的人的钱数减去,买的衣服数 \(+1\)。但是修改后可能会乱掉(值域相交)。
那么就将 \([c,2c]\) 间的数暴力修改,插入左儿子。

浙公网安备 33010602011771号