笛卡爾樹


笛卡爾樹

大部分內容來自 OI-WIKI

定義:

笛卡爾樹是一種二叉樹,每一個結點由一個鍵值二元組 \((k,w)\) 構成。

要求 \(k\) 滿足二叉搜索樹的性質,而 \(w\) 滿足堆的性質

如果笛卡爾樹的 \(k,w\) 鍵值確定,\(k,w\) 互不相同,那么這個笛卡爾樹的結構是唯一的。

上面這棵笛卡爾樹相當於把數組元素值當作鍵值 \(w\) ,把數組下標當作鍵值 \(k\) .

顯然可以發現:這棵樹的鍵值 \(k\) 滿足二叉搜索樹的性質,而鍵值 \(w\) 滿足小根堆的性質。

其實這是一種特殊的笛卡爾樹,鍵值 \(k\) 正好對應數組下標。更一般的情況則是任意二元組構建的笛卡爾樹。

構建:

棧構建:

將元素按鍵值 \(k\) 排序(也就是下標),先后插入當前的笛卡爾樹中,那么我們每次插入的元素必然在這個樹的右鏈(從根節點一直往右子樹走形成的鏈) 的末端。

執行這樣一個過程:

  • 從下往上比較右鏈節點與當前加入節點 \(x\) 的鍵值 \(w\)
  1. 如果找到右鏈上的節點 \(u\) 滿足 \(w_u<w_x\) ,那么把 \(x\) 接到 \(u\) 的右兒子上,而以 \(u\) 為根的右子樹變成了 \(x\) 的左子樹,然后停止加入過程。

  2. 如果沒有找到右鏈上的節點,那么 \(x\) 就是新根,把原來的樹當成 \(x\) 的左兒子。

每次添加值都需要進行一次這樣的比較。

過程圖:

顯然,每個數最多進出右鏈一次,這個過程可以用棧進行維護,棧中維護當前笛卡爾樹右鏈上的節點,不在右鏈就彈出,時間復雜度為 \(O(n)\)

代碼:

for(int i=1;i<=n;i++){
    while(top&&a[stk[top]]>a[i]) son[i][0]=stk[top--];//當前節點成為了i的左兒子 
    if(stk[top]) son[stk[top]][1]=i;//還存在,說明沒有遍歷到頭,因此該點對應的右兒子設置成i
    stk[++top]=i;//新兒子進節點
}

例題:

P5854 笛卡爾樹

題意:

給定一個 \([1-n]\) 的排列 \(p\), 構建一顆二叉樹滿足:

  1. 每個節點編號滿足二叉搜索樹性質
  2. 節點 \(i\) 的權值為 \(p_i\) ,每個節點權值滿足小根堆性質。

分別求每個節點左 \(/\) 右兒子乘節點編號的異或值之和

分析:

就是構建一顆笛卡爾樹,然后計算就行了:

代碼:

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=1e7+5;

int n;
int a[N],son[N][2]; 
int sta[N],top;
int rans,lans;

int read(){//1e7的讀入....記得快讀
    int res=0; char ch=getchar();
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch<='9'&&ch>='0'){
        res=(res<<1)+(res<<3)+ch-'0'; ch=getchar();
    }
    return res;
}

signed main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        a[i]=read();
        while(a[sta[top]]>a[i]&&top) son[i][0]=sta[top--];
        if(sta[top]) son[sta[top]][1]=i;
        sta[++top]=i;
    }
    for(int i=1;i<=n;i++){
        lans=lans^(i*(son[i][0]+1));
        rans=rans^(i*(son[i][1]+1));
    }
    cout<<lans<<" "<<rans<<endl;    
    system("pause");

    return 0;
}

這里要注意的是:

  1. 棧中存儲的每個節點的標號,並不是該節點對應的值

  2. 一定要弄清楚左右兒子的賦值情況。

性質(小根笛卡爾樹):

  • 以點 \(u\) 為根的子樹是一段連續極長區間,\(w_x\) 是區間的最小值,區間在保證最小值不變的情況下不能再向兩邊延長。

  • 區間 \([a,b]\) 的最小值為 \(w_{lca_{a,b}}\)

  • 對於有序數列 \(x\) 及隨機排列 \(y\) ,笛卡爾樹的期望高度為:
    \(E(dep_i)=H(i)+H(n-i+1)\) ,其中調和級數 \(H(n)=\sum_{i=1}^x \frac{1}{i}\)
    關於這個可以看看 [AGC028B] Removing Blocks 這道題,我在博客里也寫了題解。

總結:

笛卡爾樹主要用於解決最大/最小值問題,以及通過記錄時間關於插入刪除的問題


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM