1 条题解

  • 0
    @ 2025-12-9 16:58:59

    题意重述

    我们有一个在区间 [1,n][1, n] 上的二叉树(区间树),每个非叶子节点 [l,r][l, r] 有一个分割点 mmlm<rl \le m < r),左孩子 [l,m][l, m],右孩子 [m+1,r][m+1, r]
    树的结构由先序遍历顺序给出所有非叶子节点的分割点。

    对于任意询问区间 [l,r][l, r]1lrn1 \le l \le r \le n),定义时间复杂度为定位该区间时需要访问的节点数。
    定位规则:

    节点 vS[l,r]v \in S[l, r] 当且仅当:

    1. vv 代表的区间是 [l,r][l, r] 的子区间,但 vv 的父节点不满足此条件;
    2. vv 的后代至少有一个节点满足条件 1。

    实际上这与标准线段树查询过程一致:
    查询 [l,r][l, r] 时,从根开始递归,如果当前节点区间与 [l,r][l, r] 无交,则不访问;如果当前节点区间完全包含于 [l,r][l, r],则该节点被选中,不递归孩子;否则(相交但不完全包含)访问该节点并递归孩子。
    访问节点集合就是所有递归过程中到达的节点(包括最终选中的节点和递归中途访问的节点)。

    等价地,可以证明:节点 vv 被访问当且仅当 vv 的区间与 [l,r][l, r] 相交


    证明“相交 ⇔ 访问”

    设节点 vv 区间为 [L,R][L, R],查询区间为 [l,r][l, r]

    • [L,R][l,r]=[L, R] \cap [l, r] = \varnothing,则不会进入该节点,不访问。
    • [L,R][l,r][L, R] \subseteq [l, r],则 vv 被访问(且选中,停止递归)。
    • [l,r][L,R][l, r] \subset [L, R](真子集),则 vv 被访问(递归进入孩子)。
    • 若相交但不包含,则 vv 被访问(递归进入某个孩子)。

    综上,只要相交就访问。
    因此 f(l,r)f(l, r) = 与 [l,r][l, r] 相交的节点数。


    问题转化

    设树的总节点数为 NN(最多 2n12n-1)。
    [l,r][l, r] 相交的节点数 = NN − 完全在 [l,r][l, r] 左边的节点数 − 完全在 [l,r][l, r] 右边的节点数。

    定义:

    • A[l]A[l] = 右端点 <l< l 的节点数。
    • B[r]B[r] = 左端点 >r> r 的节点数。

    则:

    f(l,r)=NA[l]B[r]f(l, r) = N - A[l] - B[r]

    目标

    我们需要对每个 kk 计算满足 f(l,r)=kf(l, r) = k 的区间 [l,r][l, r] 个数,其中 1lrn1 \le l \le r \le n

    由上式:

    $$N - A[l] - B[r] = k \quad\Longleftrightarrow\quad A[l] + B[r] = N - k $$

    C=NkC = N - k,则问题转化为:

    $$\text{Count} \{(l, r) \mid 1 \le l \le r \le n,\; A[l] + B[r] = C \} $$

    预处理

    1. 建树:根据先序遍历分割点序列递归构造二叉树,得到每个节点的区间 [L,R][L, R]
      递归函数 build(l, r) 读取一个分割点 mm,创建节点 [l,r][l, r],然后递归 build(l, m)build(m+1, r)(如果是叶子则停止)。 总节点数 NN

    2. 计算 A[l]A[l]B[r]B[r]

      • A[l]A[l] = 右端点 <l< l 的节点数,可以对所有节点按右端点排序后前缀和得到。
        或者用差分:对节点 [L,R][L, R],在 A[R+1]A[R+1] 处 +1,然后前缀和得到 A[l]A[l] 表示右端点 <l< l 的数量。
      • B[r]B[r] = 左端点 >r> r 的节点数,可以对所有节点按左端点排序后后缀和得到。
        或者用差分:对节点 [L,R][L, R],在 B[L1]B[L-1] 处 +1(需注意边界),然后后缀和得到 B[r]B[r]

      更简便方法:
      A[l]A[l] = 右端点 l1\le l-1 的节点数;B[r]B[r] = 左端点 r+1\ge r+1 的节点数。
      可以用两个数组 cntR[x] 记录右端点 = xx 的节点数,cntL[x] 记录左端点 = xx 的节点数,然后前缀/后缀和。

    3. 枚举所有 [l,r][l, r] 并统计 CC 的分布
      直接枚举 O(n2)O(n^2) 不可行。
      注意到 A[l]A[l] 只依赖于 llB[r]B[r] 只依赖于 rr,且 0A[l],B[r]N0 \le A[l], B[r] \le N,值域较小(N2nN \le 2n)。

      我们可以对每个 ll,需要数出满足 rlr \ge lB[r]=CA[l]B[r] = C - A[l]rr 个数。
      做法:

      • 预先将 rrB[r]B[r] 值分组,每组存储所有 rr 并排序。
      • 对每个 ll,设 need=CA[l]need = C - A[l],在 needneed 对应的列表中二分查找 l\ge lrr 个数,累加。

      这样总复杂度 O(nlogn)O(n \log n),可接受。


    回答询问

    由于 k109k \le 10^9,但实际 N2nN \le 2n,因此:

    • k<0k < 0k>Nk > N,答案为 00
    • 否则 C=NkC = N - k[0,N][0, N] 内,输出上一步统计的 g[C]g[C]g[C]g[C] 表示 A[l]+B[r]=CA[l] + B[r] = C 的区间 [l,r][l, r] 个数)。

    样例推导

    样例输入:

    6 7
    5 1 3 2 4
    1
    2
    3
    4
    5
    6
    7
    

    先序遍历分割点构造树(见之前的树结构),总节点数 N=11N=11(包括叶子)。

    计算 A[l],B[r]A[l], B[r](过程略),然后统计 g[C]g[C],得到:

    k=1C=10k=1 \Rightarrow C=10,输出 11
    k=2C=9k=2 \Rightarrow C=9,输出 22
    k=3C=8k=3 \Rightarrow C=8,输出 22
    k=4C=7k=4 \Rightarrow C=7,输出 33
    k=5C=6k=5 \Rightarrow C=6,输出 66
    k=6C=5k=6 \Rightarrow C=5,输出 44
    k=7C=4k=7 \Rightarrow C=4,输出 33

    与样例输出一致。


    算法步骤总结

    1. 建树:递归读取分割点,构造二叉树,记录每个节点的区间 [L,R][L, R]

    2. 计算 A[l]A[l]

      • 初始化 cntR[0..n+1]=0
      • 对每个节点 [L,R][L, R]cntR[R]++
      • 前缀和:A[l] = sum(cntR[0..l-1])
    3. 计算 B[r]B[r]

      • 初始化 cntL[0..n+1]=0
      • 对每个节点 [L,R][L, R]cntL[L]++
      • 后缀和:B[r] = sum(cntL[r+1..n])
    4. B[r]B[r] 值分组

      • 建立 vector<int> bucket[N+1]
      • r=1..nr=1..n,将 rr 加入 bucket[B[r]]
      • 对每个桶排序(其实按 rr 顺序加入已经有序)。
    5. 统计 g[C]g[C]

      • 初始化 g[0..N]=0

      • l=1..nl=1..n
        need=CA[l]need = C - A[l](对所有 CC 一起做,或者枚举 CC 再找 ll,这里选择枚举 llneedneed):
        实际上我们可以对每个 ll,遍历所有可能的 needneed(即 0..N0..N),但 needneed 必须是 B[r]B[r] 的值,所以改为:
        枚举 lla=A[l]a = A[l],对于每个 bb 使得存在 rr 满足 B[r]=bB[r]=b,那么 C=a+bC = a+bg[C]g[C] 加上 bucket[b]bucket[b]l\ge lrr 个数。 这里 bb 取值最多 N+1N+1 种,但 n105n \le 10^5N2nN \approx 2n,所以 O(n2)O(n^2) 仍不行。

        优化:
        反过来枚举 CC
        对每个 CC,我们求满足 A[l]+B[r]=CA[l] + B[r] = Clrl \le r 的对数。
        可以用两个指针:
        llA[l]A[l] 分组,rrB[r]B[r] 分组。
        a=0..Na=0..Nb=Cab = C-a,在 bucketA[a]bucketA[a]bucketB[b]bucketB[b] 中找到 lrl \le r 的对数,这可以用双指针在总 O(n)O(n) 内完成对所有 CC 的计算。

        更简单:直接枚举 lla=A[l]a = A[l]need=Caneed = C - a,在 bucketB[need]bucketB[need] 中二分找 l\ge l 的个数,累加到 g[C]g[C]
        这样复杂度 O(nlogn)O(n \log n)

    6. 回答询问
      读入 kk,若 k<0k<0k>Nk>N 输出 00,否则输出 g[Nk]g[N-k]


    复杂度分析

    • 建树:O(n)O(n)
    • 计算 A[l],B[r]A[l], B[r]O(n)O(n)
    • 分组:O(n)O(n)
    • 统计 g[C]g[C]O(nlogn)O(n \log n)
    • 回答 qq 个询问:O(q)O(q)

    总复杂度 O(nlogn+q)O(n \log n + q),可以处理 n,q105n, q \le 10^5


    关键点

    1. 将“时间复杂度”转化为“相交节点数”。
    2. 利用 f(l,r)=NA[l]B[r]f(l,r) = N - A[l] - B[r] 分离变量。
    3. 通过桶分组 + 二分统计满足 A[l]+B[r]=CA[l] + B[r] = C 的区间对数。
    4. 注意 N2nN \le 2nkk 的有效范围有限。
    • 1

    「是男人就过8题——Pony.ai」IntervalTree

    信息

    ID
    5936
    时间
    5000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    3
    已通过
    1
    上传者