zoukankan      html  css  js  c++  java
  • 单调栈的应用 --- 笛卡尔树与虚树

    笛卡尔树

    何为笛卡尔树?

    对于一组关系(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


  • 相关阅读:
    POJ 2528 Mayor's posters 线段树+离散化
    Leveldb之version与version_set详细对比
    rocksdb编译测试的正确姿势
    ycsb使用方法
    memalign vs malloc
    四种监听器(自身类,外部类,内部类,匿名类)
    数据库基础
    哈希索引和Btree索引
    哈希表hashtable
    抽象类和接口
  • 原文地址:https://www.cnblogs.com/reverymoon/p/9525764.html
Copyright © 2011-2022 走看看