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 }
posted @ 2018-10-12 13:35  阿波罗2003  阅读(...)  评论(... 编辑 收藏