【洛谷 P3224】 [HNOI2012] 永无乡
题目大意:
给你一张图,告诉你点,边的关系,每个点有一个重要度排名,现在有两个操作:
1:在两个点之间连一条边
2:询问某个点所在的连通块中重要度排名第 k 的点的编号
思路:
看到这种需要动态插入某些东西,又要维护某一个范围内的第 k 小值,第一时间想到平衡树,然后维护连通块肯定就是并查集了。
我们把每个连通块用一棵平衡树来维护,每次并查集的时候暴力将两棵平衡树合并。
注意要将小的平衡树合并到大的里面,这样复杂度就是小的平衡树的大小,其实就是启发式合并,也就是我们在并查集中所说的按秩合并,时间复杂度均摊好像是
O
(
l
o
g
N
)
O(log~N)
O(log N)吧
然后每次合并的时候将小的平衡树的数据清空。
直接上
f
h
q
T
r
e
a
p
fhq ~~~~Treap
fhq Treap
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
#include<ctime>
#define r register
#define rep(i,x,y) for(r ll i=x;i<=y;++i)
#define per(i,x,y) for(r ll i=x;i>=y;--i)
using namespace std;
typedef long long ll;
const ll V=4e5+1;
ll fa[V>>1],n,m,q,id[V>>1],root,cnt;
inline ll in()
{
ll res=0,f=1;
char ch;
while((ch=getchar())<'0'||ch>'9')
if(ch=='-') f=-1;
res=res*10+ch-48;
while((ch=getchar())>='0'&&ch<='9')
res=res*10+ch-48;
return res*f;
}
inline void put(ll x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) put(x/10);
putchar(x%10+48);
}
struct fhqtreap
{
ll lc,rc,pos;
ll siz,val;
}treap[V];
void update(ll x) { treap[x].siz=treap[treap[x].lc].siz+treap[treap[x].rc].siz+1; }
void newnode(ll x)
{
treap[++cnt].val=x;
treap[cnt].siz=1;
treap[cnt].pos=rand();
id[x]=cnt; //记录每个点原来的编号
treap[cnt].lc=treap[cnt].rc=0;
}
void split(ll now,ll k,ll &x,ll &y)
{
if(now==0)
{
x=y=0;
return ;
}
if(k>=treap[now].val)
{
x=now;
split(treap[now].rc,k,treap[now].rc,y);
}
else
{
y=now;
split(treap[now].lc,k,x,treap[now].lc);
}
update(now);
}
ll make(ll x,ll y)
{
if(x==0||y==0) return x+y;
if(treap[x].pos<treap[y].pos)
{
treap[x].rc=make(treap[x].rc,y);
update(x);
return x;
}
else
{
treap[y].lc=make(x,treap[y].lc);
update(y);
return y;
}
}
void add(ll &root,ll k)
{
ll x,y,v=treap[k].val;
x=y=0;
split(root,v,x,y);
root=make(make(x,k),y);
}
ll K(ll now,ll k)
{
while(1+1==2)
{
if(k<=treap[treap[now].lc].siz) now=treap[now].lc;
else if(k==treap[treap[now].lc].siz+1) return treap[now].val;
else k-=(treap[treap[now].lc].siz+1),now=treap[now].rc;
}
// return now;
}
ll find(ll x){ return fa[x]==x?x:fa[x]=find(fa[x]); } //并查集的基本操作
void dfs(ll x,ll &y)
{
if(x==0) return ;
dfs(treap[x].lc,y);
dfs(treap[x].rc,y);
treap[x].lc=treap[x].rc=0;
// treap[x].siz=1;
add(y,x);
return ;
}
ll merge(ll x,ll y)
{
if(treap[x].siz>treap[y].siz) swap(x,y); //启发式合并
dfs(x,y);
return y;
}
int main()
{
srand(time(0));
n=in(),m=in();
rep(i,1,n)
{
fa[i]=i;
ll x;
x=in();
newnode(x);
}
rep(i,1,m)
{
ll x,y;
x=in(),y=in(); //合并平衡树
if(find(x)==find(y)) continue ;
root=merge(fa[x],fa[y]);
fa[find(x)]=fa[find(y)]=root; //更改集合的标志
fa[root]=root;
}
q=in();
while(q--)
{
char ch;
ll x,y;
cin>>ch;
x=in(),y=in();
if(ch=='B')
{
if(find(x)==find(y)) continue ;
root=merge(fa[x],fa[y]);
fa[find(x)]=fa[find(y)]=root;
fa[root]=root;
}
else
{
x=find(x);
if(treap[x].siz<y) printf("-1\n");
else put(id[K(x,y)]),putchar(10);
}
}
return 0;
}