模板 - 左偏树
这个并不是左偏树+并查集。
左偏树+并查集里面,每个节点是主要的元素,要关心的节点当前处在哪棵左偏树的管辖内。
而纯粹的左偏树里面,左偏树本身是主要的元素,只关心左偏树本身的合并。
个堆合并了之后,把其中一个完全加入另一个,空的堆就自己去新建一个位置。
多套一层指针来记录各个工厂对应的树现在到了哪里。也就是编号为x的工厂实际是对应哪一棵左偏树。
缺点是删除的节点留在那里占位置。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
const int MAXN=1000005;
int tot,v[MAXN],l[MAXN],r[MAXN],d[MAXN];
int gc[MAXN]; //gc[i]表示编号为i的工厂所在的左偏树是哪个x
class Leftist_Tree {
int _Merge(int x,int y) {
if(!x||!y)
return x+y;
if(v[x]<v[y])
swap(x,y);
r[x]=_Merge(r[x],y);
if(d[l[x]]<d[r[x]])
swap(l[x],r[x]);
d[x]=d[r[x]]+1;
return x;
}
int Build(int val=-1) {
tot++;
l[tot]=r[tot]=d[tot]=0;
v[tot]=val;
return tot;
}
public:
void Init(int n) {
tot=n;//当前树的上界到了哪个
for(int i=1; i<=n; i++) {
l[i]=r[i]=d[i]=0;
v[i]=-1;
}
l[0]=r[0]=-1;
d[0]=0;
v[0]=-1;
}
void Push(int x,int val) {
int gcx=gc[x];
int rt=Build(val);
rt=_Merge(rt,gcx);
gc[x]=rt;
}
int Pop(int x) {
//把x工厂的左偏树的顶端弹出,没有顶端弹出-1
int gcx=gc[x];
if(v[gcx]==-1)
return -1;
int res=v[gcx];
gc[x]=_Merge(l[gcx],r[gcx]);
return res;
}
void Merge(int x,int y) {
//把编号x与编号y的工厂合并到x工厂
int gcx=gc[x];
int gcy=gc[y];
gc[x]=_Merge(gcx,gcy);
gc[y]=Build();
}
} lt;
int main() {
#ifdef Yinku
freopen("Yinku.in","r",stdin);
#endif // Yinku
int n,m;
scanf("%d%d",&n,&m);
lt.Init(n);
for(int i=1; i<=m; i++) {
int ins;
scanf("%d",&ins);
switch(ins) {
case 1: {
int x,v;
scanf("%d%d",&x,&v);
lt.Push(x,v);
break;
}
case 2: {
int x,y;
scanf("%d%d",&x,&y);
lt.Merge(x,y);
break;
}
case 3: {
int x;
scanf("%d",&x);
printf("%d\n",lt.Pop(x));
break;
}
}
}
}