洛谷 P1377 [TJOI2011]树的序 解题报告
P1377 [TJOI2011]樹的序
題目描述
眾所周知,二叉查找樹的形態和鍵值的插入順序密切相關。準確的講:1、空樹中加入一個鍵值\(k\),則變為只有一個結點的二叉查找樹,此結點的鍵值即為\(k\);2、在非空樹中插入一個鍵值\(k\),若\(k\)小于其根的鍵值,則在其左子樹中插入\(k\),否則在其右子樹中插入\(k\)。
我們將一棵二叉查找樹的鍵值插入序列稱為樹的生成序列,現給出一個生成序列,求與其生成同樣二叉查找樹的所有生成序列中字典序最小的那個,其中,字典序關系是指對兩個長度同為\(n\)的生成序列,先比較第一個插入鍵值,再比較第二個,依此類推。
輸入輸出格式
輸入格式:
第一行,一個整數,\(n\),表示二叉查找樹的結點個數。第二行,有\(n\)個正整數,\(k_1\)到\(k_n\),表示生成序列,簡單起見,\(k_1\)~\(k_n\)為一個1到\(n\)的排列。
輸出格式:
一行,\(n\)個正整數,為能夠生成同樣二叉查找數的所有生成序列中最小的。
說明
對于20%的數據,n ≤ 10。
對于50%的數據,n ≤ 100。
對于100%的數據,n ≤ 100,000。
先化簡一下模型,我們把\(BST\)樹建好,然后輸出中序遍歷即是答案。
然而,直接建\(BST\)是很容易退化成鏈的。
做過題目多的人可能聽說過,這個叫笛卡爾樹
笛卡爾樹是一種既滿足堆性質又滿足二叉排序樹性質的樹。
方法一:普通笛卡爾樹的建樹方法
現在有一個序列按二叉排序樹的關鍵字\(Key\)從小到大有序,序列中包含小跟堆的關鍵字\(Index\)關鍵字,在\(O(N)\)的時間建樹。
按原數列順序一個一個將節點加入笛卡爾樹,可以確定一定是從鏈的右邊向下找到一個\(Index\)大于這個點的節點,把這個節點位置占據,然后置讓Ta當自己的左兒子即可。
用棧把最右邊的鏈存下來,棧頂為右邊最底的那個點,加入時邊彈出邊向上找,更新好位置關系后把自己存進棧即可。
code:
#include <cstdio> #include <algorithm> #define ls t[now].ch[0] #define rs t[now].ch[1] #define f t[now].par const int N=100010; struct node {int dat,index;bool friend operator <(node n1,node n2){return n1.dat<n2.dat;} }a[N]; struct BST {int ch[2],dat,index,par;//左右兒子,BST域,堆域,父親 }t[N]; int tot=0,n,s[N]; void connect(int fa,int now,int typ) {f=fa;t[fa].ch[typ]=now; } void dfs(int now) {if(!now) return;printf("%d ",t[now].dat);dfs(ls);dfs(rs); } int main() {scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",a+i);a[i].index=i;}std::sort(a+1,a+1+n);for(int i=1;i<=n;i++){int last=0;while(tot&&t[s[tot]].index>a[i].index)last=tot--;t[i].dat=a[i].dat;t[i].index=a[i].index;connect(s[tot],i,1);connect(i,s[last],0);s[++tot]=i;}dfs(t[0].ch[1]);return 0; }方法二:按堆性質自底向上建立。
這個方法好厲害,真的很強。
則如果兩個點的\(Key\)在“當前”數值上的差最小,那么Ta倆一定有一條邊。
按\(Index\)從大到小建立笛卡爾樹,則對\(1\)-\(Index-1\)的點中,是不會有\(Index\)的兒子的。
用\(pre[i]\)與\(suc[i]\)分別存儲\(Key\)為\(i\)的點在“當前”所相鄰的前驅和后繼。
每次處理完一個點,更新與它相連的“前驅”和“后繼”,這對應了“當前”
注意到原數據為1到n的排列,可以用桶排,比較快。
Code:
#include <cstdio> const int N=100010; int a[N],b[N],L[N],R[N],pre[N],suc[N],n; void dfs(int now) {if(!now) return;printf("%d ",a[now]);dfs(L[now]);dfs(R[now]); } int main() {scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",a+i);b[a[i]]=i;pre[i]=i-1,suc[i]=i+1;}for(int i=n;i>1;i--){int pree=pre[a[i]],succ=suc[a[i]];if(b[pree]>b[succ])R[b[pree]]=i;elseL[b[succ]]=i;suc[pree]=succ;pre[succ]=pree;}dfs(1);return 0; }2018.6.19
轉載于:https://www.cnblogs.com/butterflydew/p/9199591.html
總結
以上是生活随笔為你收集整理的洛谷 P1377 [TJOI2011]树的序 解题报告的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软件工程网络15个人阅读作业1(2015
- 下一篇: bootstrap-table 行合并和