左偏树学习记录
左偏树有一些和时间复杂度证明有关的性质,但我们只要会用就好了
节点的距离:该节点到离它最近的叶节点的距离
节点的左子节点的距离总是大于右子节点的距离
显然节点的距离等于它右子节点的距离加1
合并(merge)
int merge(int x,int y) { //返回合并后的树的位置
if(x==0 || y==0)
return x+y; //一棵子树为空时
if(a[x]>a[y]) {
swap(x,y); //将y插入x
}
ch[x][1]=merge(ch[x][1],y); //将y并到x的右子树上
f[ch[x][1]]=x; //新的树父节点为x
if(d[ch[x][0]]<d[ch[x][1]]) //如果变为右偏树,则交换两棵子树
swap(ch[x][0],ch[x][1]);
d[x]=d[ch[x][1]]+1; //维护该节点的距离
return x;
}
删除(pop)
将该节点的左右子节点合并
void pop(int x) {
a[x]=-1;
f[ch[x][0]]=f[ch[x][1]]=0;
merge(ch[x][0],ch[x][1]);
}
由左偏树的性质可得,时间复杂度为
log(n)
log
(
n
)
我都说了只要会用就好了
例题(luogu P3377 【模板】左偏树(可并堆))
直接套模板
#include<cstdio>
#include<cstring>
#include<algorithm>
#define max(x,y) ((x)>(y) ? (x) : (y))
#define min(x,y) ((x)<(y) ? (x) : (y))
#define N 100010
#define ll long long
using namespace std;
inline int getint() {
char ch;
int p=0,t;
for(ch=getchar();ch!='-' && !(ch>='0' && ch<='9');ch=getchar());
if(ch=='-') t=-1,ch=getchar();
else t=1;
for(;ch>='0' && ch<='9';ch=getchar()) {
p=p*10+ch-48;
}
return t*p;
}
int n,m,a[N],f[N],d[N],ch[N][2];
int merge(int x,int y) {
if(x==0 || y==0)
return x+y;
if(a[x]>a[y] || (a[x]==a[y] && x>y)) {
swap(x,y);
}
ch[x][1]=merge(ch[x][1],y);
f[ch[x][1]]=x;
if(d[ch[x][0]]<d[ch[x][1]])
swap(ch[x][0],ch[x][1]);
d[x]=d[ch[x][1]]+1;
return x;
}
int getroot(int x) {
for(;f[x];x=f[x]);
return x;
}
void pop(int x) {
a[x]=-1;
f[ch[x][0]]=f[ch[x][1]]=0;
merge(ch[x][0],ch[x][1]);
}
int main()
{
n=getint();
m=getint();
for(int i=1;i<=n;i++) a[i]=getint();
memset(f,0,sizeof f);
memset(d,0,sizeof d);
d[0]=-1;
memset(ch,0,sizeof ch);
while(m--) {
int D=getint();
if(D==1) {
int x=getint();
int y=getint();
if(a[x]==-1 || a[y]==-1 || x==y) {
continue;
}
merge(getroot(x),getroot(y));
} else {
int x=getint();
if(a[x]==-1) {
puts("-1");
} else {
int y=getroot(x);
printf("%d\n",a[y]);
pop(y);
}
}
}
return 0;
}