在现实中认了无数师傅却毫无长进的GT在梦中成为了某武侠世界的神。在这个世界中初始有n个人,他们各成一派。作为世界神GT总共会进行m次操作,每次操作有如下两种情况
1 x y 表示x所在的帮派吞并了y所在的帮派,若x与y本来就处于同一个帮派则该操作无效。
2 k 表示GT想要知道当前第k大的帮派有多少人,若当前帮派数量少于k个则输出-1。
题意:校赛中文题~~
思路:并查集+区间第K大,权值线段树裸题,赛后发现了一道和这道题差不多的题,不知是巧合还是。。看了一下那道题的题解,emmm,了解了权值线段树,(赛场上还疯狂二分,真地菜),权值线段树还是比较好理解的,线段树上的一个节点的权值如果为m的话,代表当前这个节点被访问了m次,这是可以看一下左子节点和右子节点的状态,如果要找的数大于左子节点的权值就往右侧寻找,比较好理解,,然后更新一下要找的第k大的数,例如减去左子节点的权值。。(顺便了解一下主席树或者持久化这些高级数据结构的高级操作)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=3e5+5;
int fa[maxn],ran[maxn],n,m;
void init() {
for(int i=1;i<=n;i++) {
fa[i]=i;
ran[i]=0;
}
}
int fin(int x) {
if(fa[x]==x) {
return x;
}
else return fa[x]=fin(fa[x]);
}
struct node{
int l,r;
int cnt;
}t[maxn<<2];
void build(int tl,int tr,int root) {
t[root].l=tl;
t[root].r=tr;
if(tl==1)t[root].cnt=n;
else t[root].cnt=0;
if(tl==tr)return;
int mid=(tr+tl)/2;
build(tl,mid,root*2);
build(mid+1,tr,root*2+1);
}
void add(int x,int root,int c) {
t[root].cnt+=c;
if(t[root].l==t[root].r) return;
if(x <= t[root*2].r) add(x,root*2,c);
else add(x,root*2+1,c);
}
int query(int k,int rt=1) {
if(t[rt].l==t[rt].r) {
printf("%d\n",t[rt].l);
return 0;
}
if(k <= t[rt*2+1].cnt) query(k,rt*2+1);
else query(k-t[rt*2+1].cnt,rt*2);
}
int a[maxn];
int main() {
int T;
scanf("%d",&T);
while(T--) {
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=n;i++)a[i]=1;
build(1,n,1);
int ss=n;
for(int i=0;i<m;i++) {
int c;
scanf("%d",&c);
if(c==1) {
int s1,s2;
scanf("%d%d",&s1,&s2);
int x=fin(s1);
int y=fin(s2);
if(x==y)continue;
add(a[x],1,-1);
add(a[y],1,-1);
add(a[x]+a[y],1,1);
fa[y]=x;
a[x]+=a[y];
ss--;
}
else {
int k;
scanf("%d",&k);
if(k>ss) {
printf("-1\n");
continue;
}
else {
query(k);
}
}
}
}
}