1 条题解

  • 0
    @ 2025-10-27 16:00:58

    一、先搞懂“广义线段树”是啥(和普通线段树的区别)

    普通线段树的每个非叶子节点,一定把区间从正中间劈开(比如[1,4]拆成[1,2]和[3,4])。但广义线段树不一样,它的非叶子节点可以“随便拆”,只要满足左区间的右端 < 右区间的左端就行(比如[1,4]可以拆成[1,3]和[4,4])。

    但不管怎么拆,广义线段树的总节点数是固定的:2n-1个(n个叶子节点,对应n个原始元素;n-1个非叶子节点,负责拆分区间)。

    二、步骤1:构建广义线段树(把树的结构“存下来”)

    要处理后续查询,得先把这棵树的结构记录清楚,包括每个节点的:

    • 对应区间(比如节点5对应[2,3]);
    • 左孩子、右孩子(拆分后的两个子节点);
    • 父节点(用来算深度和LCA)。

    怎么构建?(用“栈”模拟,避免递归栈溢出)

    因为n可能很大(2e5),递归会栈溢出,所以用栈迭代的方式构建:

    1. 初始化栈:把根节点(对应区间[1,n],父节点为0)压入栈。
    2. 循环弹出栈顶元素:
      • 如果是叶子节点(区间左端点=右端点):直接记录它的区间和父节点,不用再拆分。
      • 如果是非叶子节点:按题目给的“划分位置m”(n-1个m,按节点标号递增顺序),把当前区间[L,R]拆成左区间[L,m]和右区间[m+1,R]。然后:
        • 给当前节点分配一个标号,记录它的区间[L,R]和父节点;
        • 先把右孩子压入栈(因为栈是“先进后出”,这样左孩子会先处理,保证“先序遍历”的顺序);
        • 再把左孩子压入栈。
    3. 重复步骤2,直到栈空,树就建好了。

    三、步骤2:预处理“深度”和“LCA”(为算距离做准备)

    节点之间的距离公式是:距离 = 深度[u] + 深度[v] - 2×深度[LCA(u,v)]。所以得先算出每个节点的深度,以及任意两个节点的最近公共祖先(LCA)

    1. 算每个节点的深度(用BFS)

    • 根节点(1号)的深度是0;
    • 用队列遍历:每个节点的左/右孩子的深度 = 父节点深度 + 1,依次记录所有节点的深度。

    2. 算LCA(用“倍增法”,快速查询)

    LCA是两个节点“最近的共同祖先”。比如节点A在第3层,节点B在第4层,它们的LCA在第2层,那A到B的路径会经过LCA。

    倍增法预处理:搞一个“跳步表”up[k][u],表示节点u往上跳2^k步到达的祖先(比如k=0是跳1步,即父节点;k=1是跳2步,即祖父节点)。

    • 先填k=0的层:up[0][u] = 父节点u
    • 再填k≥1的层:up[k][u] = up[k-1][up[k-1][u]](跳2^k步 = 先跳2^(k-1)步,再跳2^(k-1)步)。

    查LCA的过程

    • 先把两个节点拉到同一深度(比如u深、v浅,就把u往上跳,直到和v深度相同);
    • 再让两个节点一起往上跳,直到相遇,相遇的节点就是LCA。

    四、步骤3:处理查询(分解区间+算距离和)

    每个查询给u, l, r,要做两件事:

    1. 分解区间[l,r],得到S[l,r](找最少的节点)

    目标是找最少个不重叠的节点,让它们的区间拼起来正好是[l,r]。用栈迭代分解:

    • 初始化栈:把根节点(1号)和目标区间[l,r]压入栈。
    • 循环弹出栈顶元素(节点x,目标区间[cur_l, cur_r]):
      • 如果x的区间和[cur_l, cur_r]完全不重叠:跳过;
      • 如果x的区间完全包含在[cur_l, cur_r]里:把x加入S[l,r](这就是我们要的节点);
      • 否则:x的区间部分重叠,需要拆成左、右孩子处理,先压右孩子,再压左孩子(保证左孩子先处理)。
    • 重复直到栈空,S[l,r]就得到了。

    2. 算距离和

    对S[l,r]里的每个节点v,用公式距离 = 深度[u] + 深度[v] - 2×深度[LCA(u,v)]算出距离,所有距离加起来就是答案。

    样例拆解(帮你理解每一步)

    看样例输入:

    10
    3 1 2 9 6 4 5 7 8
    3
    7 6 7
    18 4 5
    14 5 6
    

    输出是7113。以第一个查询7 6 7为例:

    • 分解[6,7]得到S[l,r](假设是节点A和节点B);
    • 算u=7到A的距离,加u=7到B的距离,总和是7。

    具体数值怎么来的?靠前面的步骤一步步算:树的结构构建后,每个节点的深度、LCA都能确定,代入公式就得到结果了。

    • 1

    信息

    ID
    4247
    时间
    3000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    11
    已通过
    1
    上传者