树上笛卡尔树(点权Kruskal重构树)

题意

给一 k e ke ke 树,求所有路径 ( x , y ) (x,y) (x,y),满足 x → y x \to y xy 中,编号最小为 x x x,最大为 y y y 的数量。 n ≤ 2 × 1 0 6 n \leq 2 \times 10^6 n2×106

题解

考虑到最大最小值问题,我们想到用笛卡尔树,而树上的笛卡尔树形态则类似 K r u s k a l Kruskal Kruskal 重构树( x , y x,y x,y 在重构树上的 L c a Lca Lca 为原树上的路径 x → y x \to y xy 上的最值)。
至于构建:
对于一 k e ke ke 从大到小( x , y x,y x,y L c a Lca Lca 为原树路径上的最大值)的树,我们按照编号从小到大,假设当前的点为 p p p,设 p p p 连向的点中,编号小于 p p p,( v < p , v ∈ p v<p,v \in p v<p,vp)的点的祖先的祖先为 p p p,( x = g e t ( v ) , f a [ x ] = p x=get(v),fa[x]=p x=get(v),fa[x]=p)。

于是我们构建两 k e ke ke 树,一 k e ke ke 从大到小,一 k e ke ke 从小到大。

我们发现: x → y x \to y xy 合法,当且仅当 x x x 在一 k e ke ke 树上是 y y y 的祖先, y y y 在另一 k e ke ke 树上是 x x x 的祖先。

所以剩下的用 d f s dfs dfs 序,二维偏序这些随便算算即可。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,关于用 C 语言建立笛卡尔树求解区间最值问题,我可以给你一些基本的思路和代码实现。 首先,笛卡尔树是带权值的,其性质由下面的定理给出: 定理1:一个序列可以对应唯一的一棵笛卡尔树。 定理2:设在笛卡尔树上节点 u 和 v 的先后顺序为“按下标增大的顺序,在这个前提下按权值减小的顺序”,则 u 到 v(包括 u 和 v)路径上的点在原序列中构成了一个区间。 基于这两个定理,我们就可以使用笛卡尔树来解决区间最值问题,其基本思路如下: 1. 输入一个序列 a[N],构建对应的笛卡尔树。 2. 对于每个节点 u,记录其子中最小值或最大值(根据具体问题而定)。 3. 对于每个询问,找到笛卡尔树上 u 和 v 两个节点,使得它们对应的区间包含询问区间 [L,R],那么 [L,R] 中的最值就是从 u 到 v 路径上的最小值或最大值(可以使用 st 表等数据结构进行维护)。 下面是用 C 语言实现笛卡尔树的基本代码,可以参考一下: struct Node { int val, idx; int fa, ch[2]; } tree[N]; int root, idx; int build(int l, int r) { // 构建笛卡尔树并返回根节点编号 if (l > r) return -1; int x = l; for (int i = l + 1; i <= r; i++) if (tree[i].val < tree[x].val) x = i; tree[x].ch[0] = build(l, x - 1); tree[x].ch[1] = build(x + 1, r); if (tree[x].ch[0] != -1) tree[tree[x].ch[0]].fa = x; if (tree[x].ch[1] != -1) tree[tree[x].ch[1]].fa = x; return x; } 现在我回答了你的问题,如果您有任何其他问题,可以随时问我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值