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

    题目传送门

      传送点I

      传送点II

    题目大意

      给定一个(小根)斜堆的生成方式。

    1. 如果$H$为空,或者插入的数$x$的权值小于根节点的权值,那么用$x$顶替$H$的位置,然后把$H$作为它的左子树。
    2. 否则交换$H$根的左右子树,然后递归左子树。

      给定一个斜堆,元素大小分别为$0, 1, dots, n$,问字典序最小的插入序列。

      (这篇随笔可能不太严谨。)

      考虑倒着做这么一个操作。不难发现下面这两条:

    引理1 斜堆$H$中所有非叶节点必然存在左子树。

      证明 假设存在一个非叶节点$x$不存在左子树,那么它一定只存在右子树。

      显然它不可能比它右子树中某个点$y$晚插入,因为插入$y$的时候,原来的右子树变成了左子树,之后一定存在。

      同时它也不可能比它右子树中某个点$y$之前插入,因为插入$x$的时候,$y$会在$x$的左子树中,再次插入后面的点的时候,左子树一定存在。

      因此它不存在右子树。与它是非叶节点矛盾。

      然后不难得到:

    定理2.1 当新插入的点是非叶节点的时候,它的所有父节点都有右子树,当新插入的点是叶节点的时候

    定理2.2 当新插入的点是叶节点的时候,它的父节点的所有父节点都有右子树。

      证明 设新插入的点为$x$。

    • 当$x$不是叶节点的时候,它的父节点存在同时存在左右子树。假设它的$k$级祖先存在,并同时存在左右子树,当$k + 1$级祖先存在的时候,推得$k + 1$级祖先存在右子树,所以$k + 1$级祖先同时存在左右子树。因此定理2.1得证。
    • 当$x$是叶节点的时候,它的父节点原本是叶节点或只存在左子树。剩下的一样。

      然后再讨论一下还有哪些特点

    定理3 新插入的点必定满足:

    • 在最左链上
    • 不存在右子树

      这两点都比较显然。由插入做法易证。

    定理4 一个二叉树$H$能通过斜堆车插入方式得到的充分必要条件是:

    1. 满足堆性质
    2. 要么是叶节点,要么存在左子树。

      证明  必要性 第一点显然,第二点由引理1可证。

      充分性 当点数为1的时候,显然成立。假设当点数不超过$k$时成立,考虑当点数为$k + 1$的时候。

      当根节点右子树为空,因为左子树是能够构造出来,可以先建出左子树,然后再插入根节点。

      否则我们考虑当前最后一个插入的点,由定理3可以知道它存在于当前根的左子树中。

      一个插入序列在满足根节点同时存在左右子树之后,只是交替着向两个子树中插入元素。

      假设知道了左子树和右子树的插入序列(根据归纳假设我们知道它是存在的)。 

      显然插入操作是可逆的,我们交替着撤销左右子树的插入,直到根没有同时存在两棵子树,这时候根一定不存在右子树(删完最后一个点,然后交换左右子树,所以右子树为空)。

      此时把根删掉。然后可以继续按照左子树的插入序列构造剩下的左子树。

      然后我们就倒着构造出了插入序列。

    定理5 一个点能成为最后一个插入的点的充分必要条件是:

    1. 在最左链上
    2. 不存在右子树
    3. 当是叶节点时,父节点的所有父节点存在右子树,当是非叶节点时,它的所有父节点存在右子树

      证明 必要性由以上讨论可知。

      充分性 由定理4必要性可知,每个非叶点均存在左子树并且满足堆性质。

      当它是叶节点的时候把它删掉,然后交换它所有的祖先的左右子树。它的父节点要么变成叶节点要么只存在左子树。因为它父节点的祖先均在右子树,所以左子树非空。其他点不受影响,所以所有非叶节点存在左子树。显然仍然满足堆性质。由定理4可知剩下的树能够通过斜堆的插入方法得到。

      当把这个点的左子树变为它的父节点的左子树,然后把这个点删掉后,仍然满足堆性质。类似地可以证明。

      我们仔细发现,可能成为最后一个插入的点只至多可能有2个。当最左链的叶节点的父节点满足时存在两个,否则只存在一个。

      为了使得字典序最小,我们考虑讨论第一种情况。无论发现最后一个是哪一个点,撤销它的插入后,剩下的树的形态都一样。

      所以对于其中一个的合法插入序列,交换这两个数的位置,仍然成立。

      所以一定是后插入较大的数。(不然我就交换这两个数,然后可以得到字典序更小的插入序列)。

      然后做法就变得异常简单:

    1. 找到最左链上深度最大的满足条件的点
    2. 删掉它,加入到插入序列,然后交换它的所有父节点的左右子树。

    Code

     1 /**
     2  * bzoj
     3  * Problem#1078
     4  * Accepted
     5  * Time: 4ms
     6  * Memory: 1304k
     7  */
     8 #include <iostream>
     9 #include <cstdlib>
    10 #include <cstdio>
    11 using namespace std;
    12 typedef bool boolean;
    13 
    14 const int N = 1e3 + 5;
    15 
    16 int n;
    17 int ch[N][2];
    18 int fa[N];
    19 
    20 inline void init() {
    21     scanf("%d", &n);
    22     fa[0] = -1;
    23     for (int i = 1, d; i <= n; i++) {
    24         scanf("%d", &d);
    25         if (d < 100)
    26             fa[i] = d, ch[d][0] = i;
    27         else
    28             d -= 100, fa[i] = d, ch[d][1] = i;
    29     }
    30 }
    31 
    32 int res[N];
    33 inline void solve() {
    34     int m = n;
    35     int rt = 0;
    36     while (n) {
    37         int p = rt;
    38         while (ch[p][1])
    39             p = ch[p][0];
    40         int s = ch[p][0];
    41         if (!ch[s][0])
    42             p = s;
    43         res[n--] = p;
    44 
    45         int x = fa[p];
    46         if (ch[p][0])
    47             fa[ch[p][0]] = fa[p];
    48         if (x >= 0) {
    49             ch[x][0] = ch[p][0];
    50             while (~x) {
    51                 swap(ch[x][0], ch[x][1]);
    52                 x = fa[x];
    53             }
    54         } else
    55             rt = ch[p][0];
    56     }
    57     res[0] = rt;
    58     for (int i = 0; i <= m; i++)
    59         printf("%d ", res[i]);
    60 }
    61 
    62 int main() {
    63     init();
    64     solve();
    65     return 0;
    66 }
  • 相关阅读:
    [置顶] duilib优化
    cocos2dx 0.9.2 ccmenu bug
    wince 本地播放器界面
    关于变更设计
    MS SQL系统存储过程览要
    [翻译]IE8下VML的变化
    VS 2010 快捷操作
    .Net Web程序设计——通用的设计元素
    如何高效的清理系统盘空间?
    如何进行SQL性能优化?-借助Profile
  • 原文地址:https://www.cnblogs.com/yyf0309/p/9775212.html
Copyright © 2011-2022 走看看