题面:https://www.luogu.com.cn/problem/P3224
给你n个点,每次合并两个点所在集合,动态询问每个点所在集合的第k大值
看到动态第k大值,便很容易想到平衡树
因为要合并集合,就不能用普通treap,可以用splay
但这里要讲的是FHQtreap
先建n棵FHQtreap,每次合并两棵树,正确性毋庸置疑
如何实现? 时间复杂度?
、
、
启发式合并
登场
每次将节点数小的树拆成一个一个的节点依次合并到节点数大的树上,时间复杂度为
O
(
n
l
o
g
2
n
)
O(n log^2 n)
O(nlog2n)
证:对于任意一个节点
x
x
x,每一次合并,它所在树的节点树都要乘2,所以它最多被合并
l
o
g
2
n
log_2n
log2n次
(eg:第一次合并,x所在树结点个数为2,第二次合并结点个数为4,第三次合并为8…)
又因为有n个这样的结点,每次合并复杂度为
l
o
g
2
n
log_2n
log2n,所以:最坏复杂度
O
(
n
l
o
g
2
n
)
O(n log^2 n)
O(nlog2n)
C o d e Code Code
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5,Mod=1<<30;
struct FHQ{
int lc,rc,size,val,rank,fa;
FHQ(){lc=rc=size=fa=0;}
}tree[MAXN+10];
inline int read();
inline void update(int);
void Insert(int,int);
void merge(int&,int,int);
void dfs(int&,int,int);
void spilt(int,int&,int&,int);
int Query(int,int);
int main(){
freopen ("std.in","r",stdin);
freopen ("std.out","w",stdout);
srand((unsigned)time(NULL));
int n=read(),m=read();
for (register int i=1;i<=n;++i){
tree[i].rank=rand()%Mod;
tree[i].val=read();
tree[i].size=1;
}
for (register int i=1;i<=m;++i) Insert(read(),read());
m=read();
for (register int i=1;i<=m;++i){
char c=getchar();
while (c!='B' && c!='Q') c=getchar();
int x=read(),y=read();
if (c=='B') Insert(x,y);
else printf("%d\n",Query(x,y));
}
return 0;
}
inline int read(){
int x=0;
char c=getchar();
while (!isdigit(c))c=getchar();
while (isdigit(c))x=(x<<3)+(x<<1)+(c&15),c=getchar();
return x;
}
inline void update(int rt){
tree[rt].size=tree[tree[rt].lc].size+tree[tree[rt].rc].size+1;
tree[tree[rt].lc].fa=tree[tree[rt].rc].fa=rt;
}
void spilt(int rt,int &rx,int &ry,int val){
if (!rt){
rx=ry=0;
return;
}
if (tree[rt].val<=val) spilt(tree[rt].rc,tree[rx=rt].rc,ry,val);
else spilt(tree[rt].lc,rx,tree[ry=rt].lc,val);
update(rt);
}
void merge(int &rt,int rx,int ry){
if (!rx || !ry){
rt=rx+ry;
return;
}
if (tree[rx].rank<tree[ry].rank) merge(tree[rt=rx].rc,tree[rx].rc,ry);
else merge(tree[rt=ry].lc,rx,tree[ry].lc);
update(rt);
}
void Insert(int rt,int z){
while (tree[rt].fa) rt=tree[rt].fa;
while (tree[z].fa) z=tree[z].fa;
if (rt==z) return;
if (tree[rt].size<tree[z].size) swap(rt,z);
dfs(rt,z,0);
}
void dfs(int &rt,int x,int fa){
if (!x) return;
dfs(rt,tree[x].lc,x);
dfs(rt,tree[x].rc,x);
tree[x].lc=tree[x].rc=tree[x].fa=0; tree[x].size=1;
int rx=0,ry=0;
spilt(rt,rx,ry,tree[x].val);
merge(rx,rx,x);
merge(rt,rx,ry);
}
int Query(int rt,int k){
while (tree[rt].fa) rt=tree[rt].fa;
if (k>tree[rt].size) return -1;
while (k!=tree[tree[rt].lc].size+1){
if (k<=tree[tree[rt].lc].size) rt=tree[rt].lc;
else k-=tree[tree[rt].lc].size+1,rt=tree[rt].rc;
}
return rt;
}