zoukankan      html  css  js  c++  java
  • [SCOI2008]斜堆

    https://zybuluo.com/ysner/note/1222559

    题面

    斜堆是一种二叉树,且每个非根结点的值都比它父亲大。
    在斜堆(H)中插入新元素(X)的过程是递归进行的:

    • (H)为空或者(X)小于(H)的根结点时,(X)变为新的树根,而原来的树根(如果有的话)变为(X)儿子。
    • (X)大于(H)的根结点时,(H)根结点的两棵子树交换,而(X)(递归)插入到交换后的子树中。

    现给出一个大小为(n+1)的斜堆,询问该斜堆字典序最小的插入序列。

    • (nleq50)

    解析

    经过审题,我们发现每次插入元素后,该子树的根一定会有左子树,而右子树只能通过新元素经过、结点交换左右子树产生。
    然后就看不出什么了?

    一开始,我们不会求插入序列,但我们似乎可以求最后插入的那个元素。

    • 它是极左点(即从根往下走的都是左子树)。
    • 它没有右子树(因为没有新元素经过它往下走)。

    于是我们可以初步确定它是极左链上的几个元素,但是并不确定他是哪一个。
    对此可以讨论一下。设(x,y)为符合条件的两点,(x)深度大于(y)

    • 如果(x)是最后插入的:
      (deep_x>deep_y)可知,(x)插入时一定经过了(y),则(y)一定交换过左右子树。而(y)又满足最终只有右子树的性质,所以在(x)经过前,(y)只能有右子树或者没有子树。而这是不可能的。

    因而可证明最后插入的点一定是极左链上深度最小的点,或者是该点的是叶结点的左儿子。
    而为了保证字典序最小,如果能取后者就取。

    而找出最后插入的点并把它删掉后,问题就转化为规模更小的子问题,我们可以在剩下的斜堆中找出最后插入的点,以此类推。
    时间复杂度(O(nlogn))。。。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define re register
    #define il inline
    #define ll long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    int n,d[100],f[100],ls[100],rs[100],ans[100],top,flag,rt=1;
    il ll gi()
    {
      re ll x=0,t=1;
      re char ch=getchar();
      while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void dfs(re int u)
    {
      if(!rs[u])
        {
          if(ls[u]&&!ls[ls[u]]&&!rs[ls[u]])
        {
          ans[++top]=ls[u],flag=1;
          ls[u]=0;f[ls[u]]=0;
        }
          else
        {
          ans[++top]=u,flag=1;
          if(u==rt) rt=ls[u];
          ls[f[u]]=ls[u];f[ls[u]]=f[u];f[u]=0;
        }
        }
      if(flag) return;
      if(ls[u]) dfs(ls[u]);
      if(flag) {swap(ls[u],rs[u]);return;}
    }
    int main()
    {
      n=gi();
      fp(i,2,n+1)
        {
          d[i]=gi();
          if(d[i]>=100) rs[d[i]-100+1]=i,f[i]=d[i]-100+1;
          else ls[d[i]+1]=i,f[i]=d[i]+1;
        }
      while(top<=n)
        {
          flag=0,dfs(rt);
        }
      fq(i,top,1) printf("%d ",ans[i]-1);puts("");
      return 0;
    }
    
  • 相关阅读:
    区间贪心问题小结(区间选点,区间覆盖,区间选取)
    Poj-3630(字典树,水题)
    G
    hdu3460(字典树)
    HDU 5512 Pagodas(2015ACM/ICPC亚洲区沈阳站-重现赛(感谢东北大学))
    Python项目之爬取斗图网所有图片
    Python学习笔记
    c重定向函数
    3110: [Zjoi2013]K大数查询
    4826: [Hnoi2017]影魔
  • 原文地址:https://www.cnblogs.com/yanshannan/p/9352349.html
Copyright © 2011-2022 走看看