【Codechef】B-Tree

本文探讨了一类树形结构问题,即如何高效计算在给定树中多个点及其控制范围下被覆盖的节点数量。文章首先简化问题,介绍点剖与边剖的基本思想,再进一步扩展至多点情形,提出构建虚树的方法,并讨论如何通过虚树来分解原问题,最终给出一种结合DFS序和主席树实现高效查询的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

给出一棵n个点的树,边权都为1,以及m个询问,每一个询问形式如下:

给出k个点的集合S={a1,a2,,ak},以及每一个点的控制范围rai
一个点p称为被控制的,当且仅当xS,dis(x,p)rx
问有多少个点被控制。

n,Q5×104,k5×105


分析

part 1 简化的问题

不妨先看当k=1时,问题变成怎么样的了。

给出一棵树,每次询问距离点x的距离小于r的点的个数。

考虑点剖,每一个重心上存点剖子树中所有点,按照离重心距离为关键字建桶。
那么只需要在点剖树上,从x往上跳,沿路统计答案就可以了。
但是注意可能会有重复计算。但是考虑点剖树上某个点只有logn个祖先,将它们记录下来,逐个减去重复计算的就可以了。
时间复杂度O(log2n)
但是假如我们加入无用白点,那么这里可以O(1)地解决重复问题(只考虑另外两个儿子就可以了)
时间复杂度O(logn)

边剖也是类似的,不过相对来说更加好写一点。

part 2 原问题的分解

扩展到多点 虚树

k>1时,我们可以引入虚树和控制范围的概念,从而将原问题转化。

虚树的构建可以利用欧拉序,首先先将每两个相邻的关键点之间最近公共祖先都加入进去。然后维护一个栈,每加入一个点,就不断退栈,直到栈顶是它的某个祖先,然后将栈顶与这个点连边,最后将这个新点压入栈。

首先先构建出这k个点的虚树,那么虚树中的每个点x,它的控制范围lx=max0y<nlydisx,y,每个点的归属belongx自然就是任意一个取得最大lxy
为了统一,假如某个点不被控制,不妨将lx赋为0belongx赋为n
用spfa可以快速地计算出每个点的控制范围。

然后问题的答案就等于虚树上的每条边的答案之和。

单条边的处理方式

一条边的答案正着算似乎不好处理,用所有的虚树上点的控制范围点数和,减去重复的部分,也可以得到原来的答案。

  • 性质一:设(x,y)是虚树上的一条边,那么当且仅当belongxbelongy时,才会在这条边上出现重复。
    证明:显然。
    • 性质二:对于满足性质一的边,存在唯一的zpath[x,y],使得path[x,z]上的点都属x管劾,path(z,y]上的点都属y管劾。
      证明:
      不妨令depx<depy,那么
      lxdis(x,z)lydis(z,y)
      lx(depzdepx)ly(depydepz)
      depx+depy+lxly2depz
      depx+depy+lxly2depz
      由上面这条式子,就可以知道z唯一确定了一条边之间两个点的管劾范围。

    part 3 更深入的分析

    u-v之间确定点z

    通过这样的划分以后,我们计算答案的思路就变成下面两步

    • 假如某个点在新图中存在势力范围,那么统计它势力范围内的点数,将其求和。

    • 对于每条两个端点的统治势力不同的点,计算它们重叠部分并减去之。

    对于第一个问题,套用“简化的问题”的思路,就可以简单地解决了。
    对于第二个问题,实际上就是要统计距离不在点z子树内,离z距离小于等于lz的点数,以及在z的子树中,离点z的距离小于等于ludis(z,u)的点数。

那么对于第二个问题比较好的解决思路就是dfs序,因为子树都是dfs序中连续的一段,而不在子树中必定是全段挖去中间的一小段。那么我们只需要按dfs序构建主席树,那么就可以快速进行统计了。

那么单次询问的时间复杂度就是的时间复杂度O(klogn)

于是最终总的时间复杂度是O(nlogn+Qklogn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值