题目描述
如题,一开始有N个小根堆,每个堆包含且仅包含一个数。接下来需要支持两种操作:

操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作)

操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作)

输入格式
第一行包含两个正整数N、M,分别表示一开始小根堆的个数和接下来操作的个数。

第二行包含N个正整数,其中第i个正整数表示第i个小根堆初始时包含且仅包含的数。

接下来M行每行2个或3个正整数,表示一条操作,格式如下:

操作1 : 1 x y

操作2 : 2 x

输出格式
输出包含若干行整数,分别依次对应每一个操作2所得的结果。

输入输出样例
输入 #1复制

5 5
1 5 4 2 3
1 1 5
1 2 5
2 2
1 4 2
2 2
输出 #1复制
1
2


就是一些堆的合并,于是我们可以用到左偏树。

写法和无旋treap差不多。

然后并查集表示当前块的位置,找到对应的堆的顶点。


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e5+10;
int ch[N][2],val[N],dis[N],f[N],n,m;
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
int merge(int x,int y){
	if(!x||!y)	return x+y;
	if(val[x]>val[y]||val[x]==val[y]&&x>y)	swap(x,y);
	ch[x][1]=merge(ch[x][1],y); f[ch[x][1]]=x;
	if(dis[ch[x][0]]<dis[ch[x][1]])	swap(ch[x][0],ch[x][1]);
	dis[x]=dis[ch[x][1]]+1;
	return x;
}
inline void pop(int x){
	val[x]=-1; f[ch[x][0]]=ch[x][0]; f[ch[x][1]]=ch[x][1];
	f[x]=merge(ch[x][0],ch[x][1]);
}
signed main(){
	cin>>n>>m; dis[0]=-1;
	for(int i=1;i<=n;i++)	scanf("%d",&val[i]),f[i]=i;
	while(m--){
		int op;	scanf("%d",&op);
		if(op==1){
			int x,y;	scanf("%d %d",&x,&y);
			if(val[x]==-1||val[y]==-1||x==y)	continue;
			int fa=find(x),fb=find(y);
			merge(fa,fb);	
		}else{
			int x;	scanf("%d",&x);
			if(val[x]==-1)	puts("-1");
			else{
				int fa=find(x); printf("%d\n",val[fa]); pop(fa);
			}
		}
	}
	return 0;
}