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;
}

原文地址:https://www.cnblogs.com/dreagonm/p/10106836.html