1 条题解
-
0
题目理解
我们有一个 个点 条边的有向图 ,定义函数 :
- 初始化 ,
- 从 到 依次枚举顶点 :
- 如果当前图 中,从 到 与从 到 的路径都存在
- 则 ,并在 中删去顶点 及与其相关的边
- 返回
需要计算:
更进一步,记删除前 条边后的图为 ,需要求出所有 的值。
关键观察
1. 函数 的本质
对于给定的 ,函数 实际上计算的是:从 出发,按照编号从小到大顺序,能够双向连通(即存在 和 的路径)且路径上所有中间点的编号都不小于当前考虑点的那些顶点 的数量。
2. 转化为点对贡献
考虑点对 (其中 ),它会对 产生贡献当且仅当:
- 存在 和 的路径
- 路径上的所有中间点的编号都不小于
算法设计
1. 定义关键量
设 表示满足以下条件的最小边权的最大值:
- 存在 和 的路径
- 路径上的边编号都不超过
特别地,当 时,
2. 贡献分析
点对 会对所有 (其中 )产生 的贡献。
3. 动态规划求解
使用类 Floyd 算法,但按顶点编号从大到小枚举中间点:
for(int k = n; k >= 1; k--) { for(int i = 1; i <= n; i++) { int t = d[i][k]; for(int j = 1; j <= n; j++) { d[i][j] = max(d[i][j], min(t, d[k][j])); } } // 计算当前k产生的贡献 for(int j = k; j <= n; j++) { int T = min(d[j][k], d[k][j]); if(T) h[T-1]++; } }4. 后缀和计算
由于贡献是累积的,需要计算后缀和:
for(int i = m; i >= 0; i--) { h[i] += h[i+1]; }
算法正确性证明
1. 动态规划的正确性
按 从大到小枚举保证了路径上的中间点编号都不小于当前考虑的起点编号,这正好符合 的定义要求。
2. 贡献计算的正确性
对于点对 , 表示最大的边权阈值,在这个阈值内双向连通性仍然保持。因此对于所有删除边数 的图,该点对都会产生贡献。
复杂度分析
时间复杂度:
- Floyd 算法:
- 贡献统计:
- 后缀和计算:
空间复杂度:
- 邻接矩阵:
- 答案数组:
对于 , 的数据范围,该算法在合理时间内可以完成计算。
代码实现要点
-
初始化:
- 邻接矩阵
d[i][j]存储边编号,无边则为 0 - 自环
d[i][i] = m + 1
- 邻接矩阵
-
动态规划更新:
- 使用
max和min组合来维护路径的最小边权最大值 - 从大到小枚举中间点
- 使用
-
贡献统计:
- 只在 时统计贡献,避免重复
- 使用
min(d[j][k], d[k][j])确保双向连通
-
答案输出:
- 后缀和计算得到最终答案
- 输出 个结果
算法标签
- 图论
- 动态规划
- Floyd算法
总结
本题的关键在于将复杂的函数计算转化为点对贡献问题,通过巧妙的动态规划和图论性质分析,避免了直接模拟函数计算的高复杂度。算法充分利用了问题的特殊结构,实现了高效求解。
- 1
信息
- ID
- 3589
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者