题意:
如题,一开始有N个小根堆,每个堆包含且仅包含一个数。接下来需要支持两种操作:
操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作)
操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作)
解题思路:裸的左偏树
左偏树学习:https://blog.csdn.net/pengwill97/article/details/82874235
左偏树的距离用处就是保证每次都是在右子树中进行插入,而右子树有时最短的。所以可以保证进行一次merge的复杂度也就O(logn),n为左偏树中的点的个数。还有注意,下面这个模板是用于顶部是最小值的情况
#include<bits/stdc++.h>
#define N 100009
using namespace std;
int val[N],fa[N],ch[N][2],dis[N];
int inline read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int getfa(int x)
{
while(fa[x]) x=fa[x];
return x;
}
int merge(int x,int y)
{
if(x==0||y==0)
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);
fa[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;
}
void del(int x)
{
val[x]=-1;
fa[ch[x][0]]=fa[ch[x][1]]=0;
merge(ch[x][0],ch[x][1]);
}
int main()
{
//freopen("t.txt","r",stdin);
int n,m,cd,x,y;
n=read();m=read();
for(int i=1;i<=n;i++) val[i]=read();
while(m--)
{
cd=read();
dis[0]=-1;
if(cd==1)
{
x=read();y=read();
if(val[x]==-1||val[y]==-1) continue;
if(x==y) continue;
merge(getfa(x),getfa(y));
}else
{
x=read();
if(val[x]==-1)
printf("-1\n");
else
{
y=getfa(x);
printf("%d\n",val[y]);
del(y);
}
}
}
return 0;
}