單調棧的應用 --- 笛卡爾樹與虛樹


笛卡爾樹

何為笛卡爾樹?

對於一組關系\(fa, ls, rs\)

滿足\(pri[fa] \geqslant max(pri[ls], pri[rs])\)

以及\(val[rs] \geqslant val[fa] \geqslant val[ls]\)

如何構建笛卡爾樹?

按照\(val\)順序順序插入\(n\)個點

那么,新插入的點一定會插入到最右邊(最大)

那么,我們維護最右鏈

同時,注意到最右鏈中\(pri\)單調

因此可以維護一個單調棧,來即時地找到插入位置

void Tree() {
    for(int i = 1; i <= n; i ++) {
        pri[i] = rand();
        while(top && pri[s[top]] > pri[i])
        ls[i] = s[top], top --;
        fa[i] = s[top]; fa[ls[i]] = i; 
        if(fa[i]) rs[fa[i]] = i;
        s[++ top] = i;
    }
}

建樹的方法2

每次選取區間最大值作為根,然后往兩邊遞歸也可以建樹

直接暴力是\(O(n^2)\)

線段樹優化一下就可以\(O(n \log n)\)


一些常見的題目:

[HNOI2016]序列

[IOI2018]meeting

luoguP4755

CF1117G

[ZJOI2012]小藍的好友

虛樹

在一棵樹中,把給定點及相關的\(lca\)求出來后按照原樹的構造連接成的樹

定理一:
樹中\(k\)個節點之間兩兩之間不同的\(lca\)至多有\(k - 1\)
證明:使用歐拉序

考慮\(dfs\)序最大的一條鏈

按照\(dfs\)序排序后,我們嘗試依次加入點\(a\)

那么,點\(a\)要么新開一條鏈,要么對\(dfs\)序最大的鏈產生影響

只要用一個單調棧來維護當前鏈即可

同時,為了方便,約定退棧連邊

具體而言,有以下幾種情況

我們假設\(v\)是棧頂元素,\(w\)是棧中排第二的元素

\(root\)\(k\)個點中\(dfs\)序最小的點(即虛樹根),\(lca\)\(v\)\(a\)的最近公共祖先

graph(1).png-15.7kB
假設原鏈的形式類似於此

第一種情況:
graph(2).png-20.5kB
這種情況下,\(v\)退棧,\((v, lca)\)需要被連接,\(lca, a\)依次進棧

第二種情況:
graph(3).png-17.9kB
\(a\)直接進棧即可

第三種情況:
graph(4).png-18.3kB
\(v\)退棧,\((v, w)\)連接,\(a\)進棧

第四種情況:
graph(5).png-22.4kB
\(v\)退棧,\((v, w)\)連接之后情況沒有什么變化
\(w\)成為新的\(v\),繼續操作直到變為情況1, 3

因此,總結一下步驟

  1. 先插入虛樹根
  2. 依次插入后\(i\)個點
  3. 求出\(a\)\(v\)\(lca\)
  4. 如果\(lca = v\),跳到第7步
  5. 如果\(dfn[w] >= dfn[lca]\)\(v\)退棧,\((v, w)\)連接,重復此步驟
  6. 如果\(dfn[w] < dfn[lca]\), \(v\)退棧,\((v, lca)\)連接,\(lca\)入棧
    否則\(v\)退棧,\((v, w)\)連接
  7. \(a\)入棧
  8. 重復第\(2\)至第\(7\)
  9. 最后處理棧中剩下的最后一條鏈

給個本人的實現吧....


inline bool cmp(int a, int b) { return dfn[a] < dfn[b]; }

//dfn數組為dfs序,dep數組為節點深度
//h數組存儲所有的關鍵點,總共有K個
//st為棧
void Vitural_Tree {
    sort(h + 1, h + K + 1, cmp);
    st[top = 1] = 1;
    for(ri i = 1; i <= K; i ++) {
        int rem = lca(st[top], h[i]);
        if(rem == st[top]) { st[++ top] = h[i]; continue; }
        while(top > 1 && dep[st[top - 1]] >= dep[rem])
        { link(st[top - 1], st[top]); top --; }
        if(dep[st[top]] > dep[rem]) link(rem, st[top]), top --;
        if(rem != st[top]) st[++ top] = rem; 
        if(h[i] != st[top]) st[++ top] = h[i];
    }
    while(top > 1) link(st[top - 1], st[top]), top --;
}

虛樹題目的顯著特征:\(\sum k \leq 3 * 10^5\)(當然有的時候並不是)

[SDOI2011]消耗戰

[HEOI2014]大工程

[HNOI2014]世界樹

PKUWC2019 你和虛樹的故事(不知道什么時候公開呢....)


有關虛樹的擴展

虛樹套數據結構:

luoguP4242 樹上的毒瘤

動態維護虛樹信息:

[SDOI2015]尋寶游戲

真.動態虛樹:(也可能是個假的

bzoj5402 f



免責聲明!

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



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