笛卡爾樹是一種同時滿足二叉搜索樹(點關系)和堆(點大小)的性質的數據結構。它的中序遍歷為原數組,且點的權值比它的孩子大(或小)。
其實可以想象一下笛卡爾樹與區間的關系。最頂上的點A就是整個區間的最大值的點,它的左孩子都在它的左邊(區間上),右孩子在它區間的右邊,然后左子樹中,又是最大值的點是根,它的左孩子在它的左邊(區間上),右孩子在它的右邊.......不斷如此。
建立這棵樹我們只要用一個單調棧來維護這棵笛卡爾樹的最右邊的節點。
我們按照點的標號index 1-n不斷插入節點,因為它的index最大,所以根據二叉搜索的性質應該要從這棵樹的最右邊插入。
又要滿足堆的性質,所以我們插入一個點A的時候,沿這棵樹的最右節點不斷向上找,直到找到第一個權值比它大的點B,根據標號,A的左孩子就是B的右孩子,而A就是B的右孩子了(這實際上似乎就是某種旋轉)
實際實現的時候我們可以用棧維護這棵樹的最右子樹,不斷彈出頂部節點以便找到第一個權值比它大的點,再更改關系即可。由於每個點都只能進出一次棧,所以復雜度為O(n).

1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define N 3000002 5 using namespace std; 6 int n,m,zhan[N],len; 7 struct data1{ 8 int l,r,p,v,id; //l左孩子,r右孩子,p父親,v權值,id標號 9 void init(){ 10 l=0,r=0,p=0,v=0,id=0; 11 } 12 }tree[N]; 13 int build(){ //建笛卡爾樹 14 len=1; 15 zhan[1]=1; //棧里面存的是點的標號Index 16 for (int i=2;i<=n;i++){ 17 while ((len>0)&&(tree[zhan[len]].v<tree[i].v)) len--; 18 if (len){ 19 tree[i].p=zhan[len]; 20 tree[tree[zhan[len]].r].p=i; 21 tree[i].l=tree[zhan[len]].r; 22 tree[zhan[len]].r=i; 23 } //用紙比划比划能夠更好自己理解這代碼 24 else { 25 tree[zhan[1]].p=i; 26 tree[i].l=zhan[1]; 27 } 28 zhan[++len]=i; 29 } 30 return zhan[1]; 31 } 32 int main(){ 33 scanf("%d%d",&n,&m); 34 for (int i=1;i<=n;i++){ 35 tree[i].init(); 36 tree[i].id=i; 37 scanf("%d",&tree[i].v); 38 } 39 return 0; 40 }