Treap:佛系平衡树,爱转不转,靠随机,靠随缘,只有单旋转。
比普通的二叉树多一个priority值,保存结点的权重用随机数生成。
如果一个节点的儿子比他的父亲优先级大【或小】,就执行一次单选转。
期望平均O(nlgn) 可以说是很随缘了。
删除稍微复杂一点:
1、叶子节点,直接消失。
2、没有左儿子/右儿子,把该节点反复执行相应旋转,转到情况1,直接删除。
3、朝优先级低的那方向转。
模板
const int maxn = 220000;
struct Treap{
int root,treapCnt,key[maxn],priority[maxn],
childs[maxn][2],cnt[maxn],size[maxn];
Treap(){
root = 0;
treapCnt = 1;
priority[0] = INT_MAX;
size[0] = 0;
}
inline void update(int x){
size[x] = size[ childs[x][0] ] + size[ childs[x][1] ] + cnt[x];
}
inline void rotate(int& x, int t){
int y = childs[x][t];
childs[x][t] = childs[y][!t];
childs[y][!t] = x;
update(x);
update(y);
x = y;
}
void _insert(int& x, int k)
{
if(x){
if(key[x] == k)
cnt[x]++;
else
{
int t = key[x] < k;
_insert(childs[x][t],k);
if(priority[ childs[x][t] ] < priority[x])
rotate(x,t);
}
}
else
{
x = treapCnt++;
key[x] = k;
if(k != 1)
cnt[x] = 1;
else
cntx
childs[x][0] = childs[x][1] = 0;
priority[x] = rand();
}
update(x);
}
void _erase(int& x, int k)
{
if(key[x] == k)
{
if(cnt[x] > 1)
cnt[x]--;
else
{
if(childs[x][0] == 0 && childs[x][1] == 0)
{
x = 0;
return;
}
int t = priority[ childs[x][0] ] > priority[ childs[x][1] ];
rotate(x,t);
_erase(x,k);
}
} else {
_erase(childs[x][key[x] < k] , k);
}
update(x);
}
//第k大 可自行改
int get_Kth(int& x,int k){
if(k <= size[ childs[x][1] ])
return get_Kth(childs[x][1],k);
k -= size[childs[x][1]] + cnt[x];
if(k <= 0)
return key[x];
else
return get_Kth(childs[x][0],k);
}
void insert(int k){
_insert(root,k);
}
void erase(int k){
_erase(root,k);
}
int getKth(int k){
return get_Kth(root,k);
}
};
然后讨论 The k-th Largest Group 这道题
题意:有一群猫,编号1~n。
有两种操作: 1、把编号为i的猫所在的集合和编号j的猫所在集合里的所有猫全部并在一起。
2、寻味集合大小排名为k的猫群有多少只猫。
分析:
1、很明显这道题必然涉及并查集操作,然而还不够,仍要维护并查集上的一个值,保存当前集合有多少只猫。维护最高祖先就可以了。
2、treap的key保存集合大小,size保存集合数量。因此在合并的时候,进行两次删除操作,值为原来两个集合大小。然后插入一个值二者大小之和的元素就可以了。
【另:节点为1其实可以不用删,因为数据合理的情况下求第k大是用不到key = 1的节点。另外开局插入时,直接令size[值为1的节点] = N 可以省去麻烦】
#include <cstdio>
#include <string>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <vector>
using namespace std;
int N = 0 , M = 0;
const int maxn = 220000;
struct Treap{
int root,treapCnt,key[maxn],priority[maxn],
childs[maxn][2],cnt[maxn],size[maxn];
Treap(){
root = 0;
treapCnt = 1;
priority[0] = INT_MAX;
size[0] = 0;
}
inline void update(int x){
size[x] = size[ childs[x][0] ] + size[ childs[x][1] ] + cnt[x];
}
inline void rotate(int& x, int t){
int y = childs[x][t];
childs[x][t] = childs[y][!t];
childs[y][!t] = x;
update(x);
update(y);
x = y;
}
void _insert(int& x, int k)
{
if(x){
if(key[x] == k)
cnt[x]++;
else
{
int t = key[x] < k;
_insert(childs[x][t],k);
if(priority[ childs[x][t] ] < priority[x])
rotate(x,t);
}
}
else
{
x = treapCnt++;
key[x] = k;
if(k != 1)
cnt[x] = 1;
else
cnt[x] = n;
childs[x][0] = childs[x][1] = 0;
priority[x] = rand();
}
update(x);
}
void _erase(int& x, int k)
{
if(key[x] == k)
{
if(cnt[x] > 1)
cnt[x]--;
else
{
if(childs[x][0] == 0 && childs[x][1] == 0)
{
x = 0;
return;
}
int t = priority[ childs[x][0] ] > priority[ childs[x][1] ];
rotate(x,t);
_erase(x,k);
}
} else {
_erase(childs[x][key[x] < k] , k);
}
update(x);
}
//第k大
int get_Kth(int& x,int k){
if(k <= size[ childs[x][1] ])
return get_Kth(childs[x][1],k);
k -= size[childs[x][1]] + cnt[x];
if(k <= 0)
return key[x];
else
return get_Kth(childs[x][0],k);
}
void insert(int k){
_insert(root,k);
}
void erase(int k){
_erase(root,k);
}
int getKth(int k){
return get_Kth(root,k);
}
};
Treap T;
int fa[maxn];
int size[maxn];
int find(int t){
int x = t;
while(x != fa[x])
x = fa[x];
return fa[t] = x;
}
void Union(int x , int y){
int fx = find(x);
int fy = find(y);
if(fx != fy)
{
if(size[fx] != 1) T.erase(size[fx]);
if(size[fy] != 1) T.erase(size[fy]);
int k = size[fx] + size[fy];
T.insert(k);
fa[fx] = fy;
size[fy] = k;
}
}
inline void init_set(){
for (int i = 0 ; i < maxn ; ++i)
{
size[i] = 1;
fa[i] = i;
}
}
int main(){
init_set();
scanf("%d%d",&N,&M);
int x = 0 , y = 0 , z = 0;
T.insert(1);
for (int i = 0 ; i < M ; ++i) {
scanf("%d",&x);
if(x == 1)
{
scanf("%d",&y);
printf("%d\n",T.getKth(y));
}
else
{
scanf("%d%d",&y,&z);
Union(y,z);
}
}
return 0;
}