P3224 [HNOI2012] 永无乡 题解
挑战最短代码(仅仅 50 行),vector大法好。
大致题意
求任意联通块第
核心思路
暴力地维护
合并就直接将小一点的平衡树上的所有元素加入到另一颗上。
可以注意到,均摊之下每个点只会被合并
这就是此题的基本做法。
但是,本题解还没还有结束。
为了缩小码量,可以使用一种新科技——vector平衡树。
平衡树代码
插入操作找第一个大于等于
void insert(int key,int id){
auto it = lower_bound(t[id].begin(),t[id].end(),key);
t[id].insert(it,key);
}
查询操作更加简单,直接输出即可。
输出之前判断一下平衡树大小,来检查是否有第
int query(int key,int id){
if(key > t[id].size())return -1;
return q[t[id][key-1]];
}
AC 代码
#include<bits/stdc++.h>
using namespace std;
int p[100010],q[100010];
int n,m;
int f[100010];
vector<int> t[100010];
int find(int x){
return (f[x] == x?x:f[x] = find(f[x]));
}
void hb(int u,int v){
f[find(u)] = find(v);
}
void insert(int key,int id){
auto it = lower_bound(t[id].begin(),t[id].end(),key);
t[id].insert(it,key);
}
int query(int key,int id){
if(key > t[id].size())return -1;
return q[t[id][key-1]];
}
void join(int u,int v){
int fu = find(u),fv = find(v);
if(t[fu].size()>t[fv].size())swap(u,v);
fu = find(u),fv = find(v);
for(int v:t[fu])insert(v,fv);
t[fu].clear();
hb(u,v);
}
int u,v;
int main(){
cin>>n>>m;
for(int i = 1;i <= n;i++){
cin>>p[i];
q[p[i]] = i;
f[i] = i;
insert(p[i],i);
}
for(int i = 1;i <= m;i++){
cin>>u>>v;
if(find(u)!=find(v))join(u,v);
}
int q;
cin>>q;
while(q--){
char c;
cin>>c>>u>>v;
if(c == 'Q')cout<<query(v,find(u))<<endl;
else if(find(u) != find(v))join(u,v);
}
return 0;
}