P2475 [SCOI2008]斜堆(递归模拟)
思路
可并堆真是一种神奇的东西
不得不说这道题是道好题,虽然并不需要可并堆,但是能加深对可并堆的理解
首先考虑斜堆的性质,斜堆和左偏树相似,有如下的性质
- 一个节点如果有右子树,就一定有左子树
- 最后插入的节点一定没有右子树
然后考虑倒序删除节点就可以做了
对一个节点,如果它没有右子树,它就可能是最后插入的点,但是显然还有其他情况
如果它的左子树中还有满足条件的点,则取深度最浅的节点即可,考虑小于等
于当前根的权值的情况时,取深度最深会有问题,可以自行画图
然后如果左子树中只有一个节点,那么两个节点都可以,为保证字典序最小,把权值较大的节点放在答案序列靠后的位置即可
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int lson[150],rson[150],fa[150],n,cnt,ans[150],root;
int getlast(int o){
int ans=o;
while(o!=-1){
if(rson[o]==-1){
ans=o;
break;
}
o=lson[o];
}
if(lson[o]!=-1&&lson[lson[o]]==-1)
ans=lson[o];
return ans;
}
void del(int o,int val){
while(fa[o]!=-1){
swap(lson[fa[o]],rson[fa[o]]);
o=fa[o];
}
}
int main(){
scanf("%d",&n);
cnt=n+1;
memset(ans,-1,sizeof(ans));
memset(lson,-1,sizeof(lson));
memset(rson,-1,sizeof(rson));
memset(fa,-1,sizeof(fa));
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
if(x<100){
lson[x]=i;
fa[i]=x;
}
else{
rson[x-100]=i;
fa[i]=x-100;
}
}
fa[0]=-1;
root=0;
while(cnt--){
int x=getlast(root);
ans[cnt]=x;
lson[fa[x]]=lson[x];
if(lson[x]!=-1)
fa[lson[x]]=fa[x];
del(x,x);
fa[x]=-1;
if(x==root)
root=lson[x];
}
for(int i=0;i<=n;i++)
printf("%d ",ans[i]);
return 0;
}