题目
思路
显然左偏树板子。
所谓左偏树,就是具有左偏性质的二叉树,主要操作有:
- 在指定集合插入一个元素
- 查询指定集合最…的元素
- 删除指定集合最…的元素
- 删除给定下标的元素
- 合并2个集合
注意,这里的合并操作是O(logn)
具体怎么做呢?首先,树该有的左右儿子,键值,父亲节点都要有。
同时我们定义
d
i
s
i
dis_i
disi为i到其子节点的最近叶节点的距离
那么左偏树满足:
- 键值有序
- 左子节点距离>=右子节点距离
左偏树合并使用递归完成。
显然,A,B合并,边界情况即其一为空,不妨设B为空,则返回A即可。
否则,不妨设A的根键值<B的根键值,小根堆即要將A的根作为根,然后即合并r(A),B
注意,合并之后如果r(A)>l(A)(指距离),交换左右。
还有,更新A的根的距离。
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<queue>
#include<deque>
using namespace std;
//When I wrote this code,God and I unterstood what was I doing
int tree[2000005],r[2000005],l[2000005],dis[2000005],fa[2000005],del[2000005];
int MG(int a,int b)
{
if (!a) return b;
if (!b) return a;
if (tree[a]>tree[b]) swap(a,b);
r[a]=MG(r[a],b);
if (dis[r[a]]>dis[l[a]]) swap(l[a],r[a]);
if (!r[a]) dis[a]=0;
else dis[a]=dis[r[a]]+1;
return a;
}
void build(int n)
{
int head[2000005],r=n,hb=1;
for (int i=1;i<=n;i++) head[i]=i;
for (int i=1;i<n;i++)
{
int root=MG(head[hb],head[hb+1]);
r++;
head[r]=root;
hb+=2;
}
return;
}
void Del(int x)
{
del[x]=1;
fa[x]=fa[l[x]]=fa[r[x]]=MG(l[x],r[x]);
l[x]=r[x]=dis[x]=0;
return;
}
int find(int x)
{
if (x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
int n,m;
int main()
{
cin>>n>>m;
for (int i=1;i<=n;i++) cin>>tree[i],fa[i]=i;
for (int i=1;i<=m;i++)
{
int opt;
cin>>opt;
if (opt==1)
{
int x,y;
cin>>x>>y;
if(del[x]||del[y]) continue;
x=find(x),y=find(y);
if (x!=y) fa[x]=fa[y]=MG(x,y);
}
else
{
int x;
cin>>x;
if (del[x])
{
printf("-1\n");
continue;
}
x=find(x);
cout<<tree[x]<<endl;
Del(x);
}
}
return 0;
}
//Now,only God know