1 条题解
-
0
题意理解
我们有 个节点(棉花糖),初始给定 条边(竹签),要求添加最少的边,使得图满足:
如果 ,并且有边 和 ,那么必须有边 。
换句话说,对于任意节点 ,与 相连且编号大于 的所有节点之间必须两两相连。
条件转化
这个条件等价于:对于任意 ,由 和所有与 相连且编号大于 的节点构成的子图必须是一个完全图。
也就是说,如果 与 相连(且 ),那么 之间必须两两相连。
问题转化
我们可以这样考虑: 对于每个节点 ,设 (即 的邻居中编号大于 的集合)。 那么 必须是一个完全图,否则需要补边。
因此,对于每个 ,如果 ,那么 中应该有 条边。 如果 中实际存在的边数不足,就需要补足。
高效计算方法
直接枚举每个 并检查 中的边数会超时( 不可行)。
我们可以这样优化:
按节点编号从大到小处理(从 到 )。
维护每个节点的“右邻居列表”,并按编号排序。
对于节点 ,它的 就是它的右邻居集合。
要检查 中哪些边已经存在,我们可以利用之前处理过的信息: 对于 (),边 存在当且仅当 是 的邻居(因为 ,所以 在 的右邻居列表中)。
具体算法:
读入边,用邻接表存储(只存编号大的邻居到编号小的邻居?不,我们存双向,但处理时只关心右邻居)。
对每个节点的右邻居列表排序。
从 到 处理每个节点 :
设 = 的右邻居列表。
需要的边数 = 。
实际存在的边数:对 中每对 (),检查 是否在 的右邻居列表中(可以用二分查找,因为列表有序)。
缺失的边数 = 需要的边数 - 实际存在的边数。
累加所有缺失的边数,加上初始的 条边,就是答案。
但这样检查每对 还是可能 ,最坏 。
进一步优化
我们可以用更高效的方法计算 中已经存在的边数:
对于节点 ,它的右邻居集合 已经排序。 对于每个 , 的右邻居集合 与 的交集大小,就是 在 中已经连接的邻居数(且编号大于 )。 实现细节 我们可以用 vector adj[N+1] 存储邻接表,并且每个节点的邻居列表排序。
然后对每个 :
中大于 的部分(其实存储时可以只存大于 的邻居,但输入边是双向的,我们存储时只存 的边?不,我们存储双向,但处理时过滤)。
实际计算时,我们只关心 的邻居中编号大于 的,所以可以预先对每个节点 的邻居排序,然后取后缀。
更简单的方法: 我们只考虑 的边,然后对每个 ,它的右邻居列表自然就是所有 。
这样我们只需要:
读入边 且 ,加入 。
对每个 排序。
对每个 ,计算需要的完全边数,减去已有的边数。
复杂度分析
排序所有邻居列表:。
主循环中,对每个节点 ,检查所有右邻居的邻居列表交集: 总复杂度 。 最坏情况下是 ,但实际数据中很难达到,因为度数和是 。
对于 ,这个算法在随机数据上可以接受,但最坏情况可能超时。
- 1
信息
- ID
- 4514
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者