并查集 + splay启发式合并 - AcWing 1063 - 永无乡
本题用并查集维护连通性,用splay支持在线查询第k大。为了使得splay能够完成合并操作,本题需要利用启发式的思想,即每次合并都将节点数少的splay的所有节点加入到节点数较多的splay中去。可以证明,splay的启发式合并的复杂度为\(O(nlogn)\)的。
// splay 启发式合并
#include <bits/stdc++.h>
using namespace std;
const int N = 500010;
int n,m,temp;
struct Node{
int s[2],p,v,id;
int size;
void init(int _v,int _id,int _p){
v = _v;
id = _id;
p = _p;
size = 1;
}
}tr[N];
int root[N],idx;
int f[N];
int find_f(int a){
if(f[a]==a)return a;
else return f[a] = find_f(f[a]);
}
inline int ws(int x){
return tr[tr[x].p].s[1] == x;
}
void push_up(int x){
tr[x].size = tr[tr[x].s[0]].size + tr[tr[x].s[1]].size + 1;
}
void rotate(int x){
int y = tr[x].p;
int z = tr[y].p;
int k = ws(x);
// modify z
tr[z].s[ws(y)] = x;
// modify y
tr[y].p = x;
tr[y].s[k] = tr[x].s[k^1];
// modify m
tr[tr[x].s[k^1]].p = y;
// modify x
tr[x].p = z;
tr[x].s[k^1] = y;
push_up(y);
push_up(x);
}
void splay(int x,int k,int rt){
while(tr[x].p != k){
int y = tr[x].p;
int z = tr[y].p;
if(z != k){
if(ws(x) ^ ws(y)){
rotate(x);
}else{
rotate(y);
}
}
rotate(x);
}
if(!k) root[rt] = x;
}
void insert(int v,int id,int rt){
int u = root[rt], p = 0;
while(u){
p = u;
u = tr[u].s[v > tr[u].v];
}
u = ++idx;
if(p) tr[p].s[v > tr[p].v] = u;
tr[u].init(v,id,p);
splay(u,0,rt);
}
int get_k(int k,int rt){
int u = root[rt];
while(u){
if(tr[tr[u].s[0]].size >= k){
u = tr[u].s[0];
}else if(tr[tr[u].s[0]].size + 1 == k){
return tr[u].id;
}else{
k -= tr[tr[u].s[0]].size + 1;
u = tr[u].s[1];
}
}
return -1;
}
void dfs_insert(int node,int rt){
if(tr[node].s[0]) dfs_insert(tr[node].s[0],rt);
if(tr[node].s[1]) dfs_insert(tr[node].s[1],rt);
insert(tr[node].v,tr[node].id,rt);
}
int main(){
scanf("%d%d",&n,&m);
// init
for(int i = 1; i <= n; ++i){
f[i] = root[i] = i;
scanf("%d",&temp);
tr[i].init(temp,i,0);
}
idx = n;
// get edges
while(m--){
int a,b;
scanf("%d%d",&a,&b);
a = find_f(a);
b = find_f(b);
if(a != b){
if(tr[root[a]].size > tr[root[b]].size){
swap(a,b);
}
dfs_insert(root[a],b);
f[a] = b;
}
}
// op
scanf("%d",&m);
while(m--){
char op[2];
int a,b;
scanf("%s%d%d",op,&a,&b);
if(*op == 'B'){
a = find_f(a);
b = find_f(b);
if(a != b){
if(tr[root[a]].size > tr[root[b]].size){
swap(a,b);
}
dfs_insert(root[a],b);
f[a] = b;
}
}else{
a = find_f(a);
if(tr[root[a]].size < b) puts("-1");
else printf("%d\n",get_k(b,a));
}
}
return 0;
}