笛卡尔树

1.笛卡尔树介绍

​ 笛卡尔树是一种二叉树,与平衡树和堆有着密切的联系。其每个节点由二元组 \((k,w)\) 组成。

2.笛卡尔树构造

​ 笛卡尔树的第一键值 \(k\) 应满足 \(BST\) (中序遍历有序)的性质。第二键值应满足堆(小根堆/大根堆)的性质。

一般情况下会把数组下标当作第一键值,把数组的权值当作第二键值。极少数情况下会有任意给定的 \((k,w)\)

​ 具体实现时,用单调栈维护从根节点开始的右链。我们先按照 \(k\) 排序,然后一个一个插入。这样的建树复杂度为 \(O(n)\)

for(int i=1;i<=n;i++){
    int now=top;
    while(now>0 && tr[i].w<tr[st[now]].w){
        now--;
    }
    if(now){
        tr[st[now]].ch[1]=i;
    }
    if(now<top){
        tr[i].ch[0]=st[now+1];
    }
    st[++now]=i;
    top=now;
}

3.笛卡尔树性质(小根堆为例)

  1. 以笛卡尔树上任意一点为根的子树是一段连续极长区间。\(w_u\) 是区间最小值,区间在保证最小值不变的情况下不能再向两边延伸。
  2. 区间 \([a,b]\) 的最小值为 \(w_{lca(a,b)}\)
  3. \(k,w\) 都互不相同,那么笛卡尔树形态结构唯一。
  4. 对于一个有序的 \(k\) 和无序的 \(w\) 。笛卡尔树上的任意一点期望高度为 \(E(depth_i) = H(i)+H(n-i+1)-1\) 。其中 $H(n)=\sum\limits_{i=1}^n\dfrac{1}{i} $ (调和级数)。因此笛卡尔树期望树高为 \(log\ n\)
  5. \(Treap\) 是一种特殊的笛卡尔树,可以运用 \(Treap\) 操作维护笛卡尔树。
  6. 笛卡尔树主要解决最大/最小值问题,以及(通过记录时间)关于插入删除的问题

4.例题

  1. Largest Rectangle in a Histogram

    这道题可以单调栈,但是也可以笛卡尔树。以下标为 \(k\) ,矩阵高为 \(w\) ,建立笛卡尔树(小根堆)。

    不难发现,笛卡尔树上每个点的子树大小,就是他的权值高度所能拥有的最大横向距离(最长区间就是子树大小)。故节点 \(u\) 的最大子矩阵就是 $ Size_u \times w_u$ 。

    Code:

struct Cartesian{
    int k,w;
    int ch[2];
};
Cartesian tr[DMAX];
int siz[DMAX];
void DFS(int now){
    if(!now){
        return ;
    }
    siz[now]=1;
    DFS(tr[now].ch[0]);
    siz[now]+=siz[tr[now].ch[0]];
    DFS(tr[now].ch[1]);
    siz[now]+=siz[tr[now].ch[1]];
}
int main(){
    while(scanf("%d",&n)){
        if(n==0){
            break;
        }
        for(int i=1;i<=n;i++){
            read(a[i]);
            tr[i].k=i,tr[i].w=a[i];
            tr[i].ch[0]=tr[i].ch[1]=0;
        }
        top=0;
        build();
        mem(siz,0);
        DFS(st[1]);
        ll ans=0;
        for(int i=1;i<=n;i++){
            ans=max(ans,1ll*siz[i]*tr[i].w);
        }
        printf("%lld\n", ans);
    }
    return 0;
}
  1. 笛卡尔树

    笛卡尔树板子题。没有什么奇技淫巧,直接算就行了。

  2. 树的序

    还是一个板子题,但是这次要以权值为 \(k\) 值,以下标为 \(w\) 值。注意输出时为先序遍历。

    Code:

    struct Cartesian{
        int k,w;
        int ch[2];
        int fa;
        bool operator <(const Cartesian &p) const{
            return k<p.k;
        }
    };
    Cartesian tr[DMAX];
    void DFS(int now){
        if(!now){
            return ;
        }
        printf("%d ", tr[now].k);
        DFS(tr[now].ch[0]);
        DFS(tr[now].ch[1]);
        return ;
    }
    int main(){
        read(n);
        for(int i=1;i<=n;i++){
            read(a[i]);
            tr[i].k=a[i],tr[i].w=i;
        }
        sort(tr+1,tr+n+1);
        build();
        DFS(st[1]);
        return 0;
    }
    

    4.Periodni

    ​ 笛卡尔树+树形DP

    ​ 我们以下标为 \(k\) 值,高度为 \(w\) 值建立笛卡尔树。这时,我们把整个图划分为了很多个小矩形。

    ​ 我们首先先考虑一个小情况:对于 \(n*m\) 的矩形,在其中选 \(k\) 个点的方案数。答案是:\(\binom{n}{k}\times\binom{m}{k}\times k!\) 。考虑其组合意义加以证明,在 \(n\) 行中选出 \(k\) 行,在 \(m\) 列中选出 \(k\) 列。然后对于每一种行的选择排列,都有 \(k!\) 种列的选择排列与其对应。

    ​ 现在我们考虑DP,令 \(f_{i,j}\) 表示在 \(i\) 的子树中选出 \(j\) 个点的方案数。

    ​ 我们先不考虑 \(u\) 这个节点的矩阵。先考虑其儿子。显而易见的边界条件为:\(f_{u,0}=1\) 。我们枚举他的儿子 \(v\) ,那么有转移:\(f_{u,j}=\sum\limits_{i=0}^jf_{v,i}\times f_{u,j-i}\)

    ​ 那么现在 \(f_{u,i}\) 就是从 \(u\) 的儿子中选择 \(i\) 个点的方案数。然后把这个点也考虑上(要减去已经通过儿子选择过的列)。于是有:\(f_{u,i}=\sum\limits_{j=1}^{i}f_{u,i-j}\times \binom{siz_u-(i-j)}{j}\times\binom{a_u-a_{fa_u}}{j}\times j!\)

    ​ 答案就是:\(f_{rt,k}\)

    ​ Code:

    void DFS(int now,int fa){
        siz[now]=1;
        f[now][0]=1;
        for(int i=0;i<=1;i++){
            if(ch[now][i]==0){
                continue;
            }
            DFS(ch[now][i],a[now]);
            siz[now]+=siz[ch[now][i]];
            int v=ch[now][i];
            for(int j=min(k,siz[now]);~j;j--){
                for(int l=1;l<=min(siz[v],j);l++){
                    f[now][j]=(0ll+f[now][j]+1ll*f[v][l]*f[now][j-l]%MOD)%MOD;
                }
            }
        }
        for(int i=min(siz[now],k);~i;i--){
            for(int j=1;j<=min(i,a[now]-fa);j++){
                f[now][i]=(0ll+f[now][i]+1ll*f[now][i-j]*jc[j]%MOD*C(siz[now]-(i-j),j)%MOD*C(a[now]-fa,j)%MOD)%MOD;
                f[now][i]=(f[now][i]%MOD+MOD)%MOD;
            }
        }
    }
    
posted @ 2021-10-20 20:33  Vitheon  阅读(233)  评论(0编辑  收藏  举报