1 条题解
-
0
题目解析
我们有一个无向连通图 ,有 个点、 条边(可能有重边、自环,边权为 )。然后依次添加 条边,得到 。对于每个 ,需要输出一个正整数 ,满足:
$$\left\lceil \frac{d(G_i)}{2} \right\rceil \le a_i \le 2\cdot d(G_i) $$其中 是图 的直径。即 是 的 两倍近似(误差允许在一倍到两倍之间)。
基本概念
- 距离 : 和 在图 中的最短路径长度(边数)。
- 离心率 :从 出发到最远点的距离。
- 直径 :图中任意两点间最大距离。
- 半径 :最小的离心率。
核心不等式
对于任意图 和任意顶点 ,有:
$$\frac{d(G)}{2} \le r(G) \le c_G(v) \le d(G) \le 2r(G) \le 2c_G(v) \le 2d(G). $$证明:
- 由定义直接得到。
- :设 是达到半径的顶点(即 ), 是直径的端点。由三角不等式:$$d(G)=\rho(u,w) \le \rho(u,x)+\rho(x,w) \le c_G(x)+c_G(x)=2r(G). $$
因此,任意一个顶点的离心率 已经是直径的一个两倍近似,因为:
$$\frac{d(G)}{2} \le r(G) \le c_G(v) \le d(G) \le 2c_G(v). $$所以如果我们能快速求出某个固定顶点(比如 )在每个图 中的离心率 ,直接输出它就满足要求。
朴素做法
对于每个 ,运行一次 BFS 从顶点 开始,得到最远距离 ,复杂度 ,不可接受( 最大 )。
关键观察
- 直径单调不增:加边不会让任何距离变大,所以 。
- 离心率单调不增:同样 。
利用单调性的优化
如果我们已经知道 ,并且找到某个 使得 ,那么对于所有 , 都是 的有效近似。
验证:对于 ,(因为 但我们需要下界?实际上 不一定成立,因为 可能很小而直径很大。但我们需要的是 。由 时 ,且 ,所以 。当 时 ,所以 。因此下界满足。上界: 是否成立?需要 ,因为 ,所以 。而 。因此成立。所以 在整个区间 都是合法近似。
于是我们可以:
- 维护当前左指针 。
- 用 BFS 计算 。
- 二分查找最大的 使得 (注意 需要提前知道,可以通过 BFS 计算或预处理)。
- 对于 输出 。
- 令 重复。
这样每次二分需要 次 BFS,总 BFS 次数 ,仍然过大。
更高效的分治方法
核心思想:如果 ,那么整个区间 都可以用 一次覆盖。否则,直径下降很快(至少减半),在中间分治。
定义递归函数
solve(l, r, cl, cr),其中cl = c_{G_l}(1),cr = c_{G_r}(1)已经计算好。def solve(l, r, cl, cr): if l == r: 输出 cl return if cl <= 2 * cr: for t in [l..r]: 输出 cl else: mid = (l + r) // 2 cm = bfs(mid) # 计算 c_{G_mid}(1) solve(l, mid, cl, cm) solve(mid+1, r, cm, cr)复杂度分析:
- 每次进入
else分支,意味着 ,由 和 可得 ,且实际上直径至少减半。 - 直径最大为 ,所以递归深度为 。
- 在每一层,每个区间最多被分裂一次,因此总的 BFS 调用次数为 。具体来说,递归树类似于线段树,但只有那些满足 的节点才会继续分裂,而这样的节点在每层数量有限,且总深度为 ,所以节点总数为 。
- 每次 BFS 复杂度 ,总复杂度 。
实现细节
- 固定源点 。
- 由于 ,一次 BFS 扫描所有边 是可行的。总 BFS 次数约 ,总操作约 ,可接受。
- 每次 BFS 需要重建图。我们可以预先存储所有边(初始 条 + 更新 条),在
bfs(t)中只使用前 条边。 - BFS 过程:邻接表动态构建,从顶点 开始,队列维护,记录最大距离。
正确性证明
- 当 时,对于任意 ,有 (因为 ?需要仔细: 由不等式,且 ,所以 。同时 。因此 是 的有效近似。
- 在递归过程中,每个区间都会被某个“祖先”区间覆盖,最终每个时刻 都会被输出某个 ,且 ,满足上述条件。
扩展思考
Bonus 问题:猜猜他们用了多少 CPU 天来计算所有测试点的精确直径?留给大家在评论区讨论。
- 1
信息
- ID
- 6553
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者