传送门:洛谷-左偏树(可并堆)
题意
一开始有N个小根堆,每个堆包含且仅包含一个数。接下来需要支持两种操作:
操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作)
操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作)
数据范围
对于30%的数据:N<=10,M<=10
对于70%的数据:N<=1000,M<=1000
对于100%的数据:N<=100000,M<=100000
代码
模板网上讲解到处都是。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int ch[N][2],key[N],exi[N],f[N],dis[N];
int read(){
int ss=0,r=1;char ch=getchar();
while (ch<'0' || ch>'9'){if (ch=='-')r=-1;ch=getchar();}
while ('0'<=ch && ch<='9'){ss=(ss<<3)+(ss<<1)+(ch^48);ch=getchar();}
return ss*r;
}
inline int find(int x)
{
return f[x]?find(f[x]):x;
}
inline void swa(int &x,int &y){
int t=x;x=y;y=t;
}
inline int merge(int x,int y)
{
if(x<1|| y<1) return x+y;
if(key[x]>key[y] || (key[x]==key[y] && x>y)) swa(x,y);
int &ul=ch[x][0],&ur=ch[x][1];
ur=merge(ur,y);
f[ur]=x;
if(dis[ul]<dis[ur]) swa(ul,ur);
dis[x]=dis[ur]+1;
return x;
}
inline void del(int x)
{
int ul=ch[x][0],ur=ch[x][1];
exi[x]=-1;f[ul]=0;f[ur]=0;
merge(ul,ur);
}
int main(){
n=read();m=read();
dis[0]=-1;
for(int i=1;i<=n;i++){
key[i]=read();
}
while(m--){
int op=read(),om=read();
if(op==1){
int ow=read();
if(exi[om]==-1 || exi[ow]==-1) continue;
om=find(om);ow=find(ow);
if(om==ow) continue;
merge(om,ow);
}else{
if(exi[om]==-1){
printf("-1\n");
}else{
om=find(om);
printf("%d\n",key[om]);
del(om);
}
}
}
return 0;
}