正题
题目链接:https://www.luogu.com.cn/problem/P3377
题目大意
开始时nnn个只有一个数的集合,要求支持
- 合并两个集合
- 查询一个集合中的最小值并删除
解题思路
左偏树就是维护一个满足以下性质的树
- 对于valxval_xvalx有valx<vallsxval_x<val_{ls_x}valx<vallsx且valx<valrsxval_x<val_{rs_x}valx<valrsx
- 对于任何节点有左子树的深度大于右子树的深度
每次合并时我们只需要将valvalval小的合并到valvalval大的右子树上,然后每次合并完之后就判断左右两边的深度大小。
时间复杂度O(nlogn)O(n\log n)O(nlogn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m,val[N];
struct Left_tree{int dis[N],fa[N],t[N][2];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);int &ls=t[x][0],&rs=t[x][1];rs=Merge(rs,y);if(dis[ls]<dis[rs]) swap(ls,rs);fa[rs]=fa[ls]=x;dis[x]=dis[rs]+1;return x;}void Del(int x){int ls=t[x][0],rs=t[x][1];fa[ls]=ls;fa[rs]=rs;val[x]=-1;fa[x]=Merge(ls,rs);}int Get(int x){return (fa[x]==x)?(x):(fa[x]=Get(fa[x]));}
}T;
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&val[i]),T.fa[i]=i;while(m--){int op,x,y;scanf("%d%d",&op,&x);if(op==1){scanf("%d",&y);if(val[x]==-1||val[y]==-1)continue;x=T.Get(x);y=T.Get(y);T.Merge(x,y);}if(op==2){if(val[x]==-1){printf("-1\n");continue;}x=T.Get(x);printf("%d\n",val[x]);T.Del(x);}}
}