bzoj 1078 [SCOI2008] 斜堆
題目傳送門
題目大意
給定一個(小根)斜堆的生成方式。
- 如果$H$為空,或者插入的數$x$的權值小於根節點的權值,那麼用$x$頂替$H$的位置,然後把$H$作為它的左子樹。
- 否則交換$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可證。
充分性 當點數為1的時候,顯然成立。假設當點數不超過$k$時成立,考慮當點數為$k + 1$的時候。
當根節點右子樹為空,因為左子樹是能夠構造出來,可以先建出左子樹,然後再插入根節點。
否則我們考慮當前最後一個插入的點,由定理3可以知道它存在於當前根的左子樹中。
一個插入序列在滿足根節點同時存在左右子樹之後,只是交替著向兩個子樹中插入元素。
假設知道了左子樹和右子樹的插入序列(根據歸納假設我們知道它是存在的)。
顯然插入操作是可逆的,我們交替著撤銷左右子樹的插入,直到根沒有同時存在兩棵子樹,這時候根一定不存在右子樹(刪完最後一個點,然後交換左右子樹,所以右子樹為空)。
此時把根刪掉。然後可以繼續按照左子樹的插入序列構造剩下的左子樹。
然後我們就倒著構造出了插入序列。
定理5 一個點能成為最後一個插入的點的充分必要條件是:
- 在最左鏈上
- 不存在右子樹
- 當是葉節點時,父節點的所有父節點存在右子樹,當是非葉節點時,它的所有父節點存在右子樹
證明 必要性由以上討論可知。
充分性 由定理4必要性可知,每個非葉點均存在左子樹並且滿足堆性質。
當它是葉節點的時候把它刪掉,然後交換它所有的祖先的左右子樹。它的父節點要麼變成葉節點要麼只存在左子樹。因為它父節點的祖先均在右子樹,所以左子樹非空。其他點不受影響,所以所有非葉節點存在左子樹。顯然仍然滿足堆性質。由定理4可知剩下的樹能夠通過斜堆的插入方法得到。
當把這個點的左子樹變為它的父節點的左子樹,然後把這個點刪掉後,仍然滿足堆性質。類似地可以證明。
我們仔細發現,可能成為最後一個插入的點只至多可能有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 }
相關推薦
bzoj 1078 [SCOI2008] 斜堆
題目傳送門 傳送點I 傳送點II 題目大意 給定一個(小根)斜堆的生成方式。 如果$H$為空,或者插入的數$x$的權值小於根節點的權值,那麼用$x$頂替$H$的位置,然後把$H$作為它的左子樹。 否則交換$H$根的左右子樹,然後遞迴左子樹。 給定一個斜堆,元素大小
[SCOI2008]斜堆
esp 大於 clu 解法 相同 復雜度 times 題目 反轉 題目大意 1.題目描述 斜堆(skew heap)是一種常用的數據結構。 它也是二叉樹,且滿足與二叉堆相同的堆性質: 每個非根結點的值都比它父親大。因此在整棵斜堆中,根的值最小。 . 但斜堆不必是平衡的,每個
【BZOJ1078】[SCOI2008]斜堆(性質題)
max turn str return name als oid 題目 getc 【BZOJ1078】[SCOI2008]斜堆(性質題) 題面 BZOJ 洛谷 題解 考慮一下這道題目的性質吧。思考一下最後插入進來的數是什麽樣子的。首先因為它是最後插入進來的,所以一定是比某個
bzoj1078: [SCOI2008]斜堆
題目 題解 考慮斜堆中最後插入的那個結點,容易發現: (1)它一定是一個極左結點(就是從根往它的路上一直都是沿著左鏈走),因為插入的時候每次都是插入到左子樹中; (2)它一定木有右子樹,因為插入的時候每次都是把原來的某棵子樹作為新結點的左子樹; 滿足(1)(2)的結點可能有多個,
[BZOJ 1076][SCOI2008]獎勵關(期望+狀壓Dp)
方便 double spa solution bsp 所有 一個 int stream Description 你正在玩你最喜歡的電子遊戲,並且剛剛進入一個獎勵關。在這個獎勵關裏,系統將依次隨機拋出k次寶物, 每次你都可以選擇吃或者不吃(必須在拋出下一個寶物之前做出選
bzoj 1079: [SCOI2008]著色方案
我沒 組合 滿足 b- col line 模擬 lose 同學 1079: [SCOI2008]著色方案 2017-08-26 Description 有n個木塊排成一行,從左到右依次編號為1~n。你有k種顏色的油漆,其中第i種顏色的油漆足夠塗ci個木塊。所有油漆
bzoj 1076: [SCOI2008]獎勵關
目前 sin 最大 ios 有一個 決定 cpp 正在 gpo Description 你正在玩你最喜歡的電子遊戲,並且剛剛進入一個獎勵關。在這個獎勵關裏,系統將依次隨機拋出k次寶物,每次你都可以選擇吃或者不吃(必須在拋出下一個寶物之前做出選擇,且現在決定不吃的寶物以後也不
並不對勁的斜堆
typedef struct nbsp 理解 none href 元素 div htm 為了反駁隔壁很對勁的太刀流,並不對勁的片手流將與之針鋒相對。 很對勁的斜堆、左偏樹簡明教程 它們是可並堆的兩種實現方式。 (假裝二叉堆只包括小根堆。) 二叉堆該如何合並?先想一種暴力的。
BZOJ 1079: [SCOI2008]著色方案(巧妙的dp)
result ret stdout 方案 code get 有一種 using spa BZOJ 1079: [SCOI2008]著色方案(巧妙的dp) 題意:有\(n\)個木塊排成一行,從左到右依次編號為\(1\)~\(n\)。你有\(k\)種顏色的油漆,其中第\(i\
深度解析(十二)斜堆
fin 內容 保存 source 測試 java版 AI root stat 斜堆(一)之 C語言的實現 概要 本章介紹斜堆。和以往一樣,本文會先對斜堆的理論知識進行簡單介紹,然後給出C語言的實現。後續再分別給出C++和Java版本的實現;實現的語言雖不同,但是原理如出一轍
BZOJ 1080: [SCOI2008]劣質編碼
技術 wid mes AC == its ++i In DC 這題我會暴力。。。如果時限 1e9 的話就可以過啦 這題 clj 也會暴力。。。然後他就秒 A 了。。。 (果然是蒟蒻 vs 大神) 大神的暴力簡直不可用語言來描述了。。。 #include <bits/
11、【堆】斜堆
print str template new oid 以及 roo cout 當前 一、斜堆的介紹 斜堆(Skew heap)也叫自適應堆(self-adjusting heap),它是左傾堆的一個變種。和左傾堆一樣,它通常也用於實現優先隊列。它的合並操作的時間復雜度也是O
BZOJ 1077: [SCOI2008]天平
題面 題意 有n個砝碼,每個砝碼的質量為1g,2g或3g,但你不知道每個砝碼的具體質量是多少,但你知道它們某幾對砝碼之間的大小關係,先將兩個砝碼A,B放在天平左邊,請你再選兩個砝碼放在天平右邊,求有多少種選法使得天平的左邊重、一樣重、右邊重?(只有結果保證惟一的選法才統計在內)
【模板】斜堆
如題這是一個模板。。。 1 #include <algorithm> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 #include <cctyp
BZOJ 4919 大根堆【set啟發式合併 維護樹上LIS】
傳送門 題意: 對於一顆有點權的樹, 如果i是j的祖先, 那麼要滿足vi > vj, 問最多可以在這棵樹上選擇多少個點可以滿足這個條件. 思路: 那麼這就是樹上lis,怎麼維護了, 每個點依舊像普
[Luogu P2507] [BZOJ 1237] [SCOI2008]配對
洛谷傳送門 題目描述 你有 nnn 個整數AiA_iAi和 nnn 個整數BiB_iBi。你需要把它們配對,即每個AiA_iAi恰好對應一個Bp[i]B_{p[i]}Bp[i]。要求所有配對的
BZOJ 1076 [SCOI2008]獎勵關【狀態壓縮】【期望DP】
基於hzwer的部落格。 lim[i]lim[i]lim[i]表示可以獲得iii得前提。 考慮倒推,當前狀態的期望=(上一個狀態的期望+這次得到的價值)/概率 #include <bits/stdc++.h> #define db double #d
[bzoj 1076][SCOI2008]獎勵關
pan 遊戲 表示 bold paper esc max efi 決定 傳送門 Description 你正在玩你最喜歡的電子遊戲,並且剛剛進入一個獎勵關。在這個獎勵關裏,系統將依次隨機拋出k次寶物, 每次你都可以選擇吃或者不吃(必須在拋出下一個寶物之前做出選擇,
BZOJ 1076([SCOI2008]獎勵關-期望dp-從後向前)
1076: [SCOI2008]獎勵關 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 328 Solved: 199 [Submit][Status][Discuss] Description 你正在玩你最喜歡的電子遊
bzoj 1076: [SCOI2008]獎勵關 (期望dp)
1076: [SCOI2008]獎勵關 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1814 Solved: 992 [Submit][Sta