1 条题解
-
0
核心思路
本题解法的核心是二进制分解思想。
1. 问题转化
设一条边的初始任务为 ,目标为 (代码中已转为 表示)。
我们想要一个排列 使得 。但一次排列作用在所有选中的边上,所以不能单独对每条边做。
这里的关键是:我们可以用多次操作,每次只改变任务编号的某一位(在二进制意义下)。
2. 二进制位独立处理
因为 ,我们可以把任务编号看成 位二进制数。
对于第 位(,因为 ,最多 5 位),我们做两次操作:
- 第一次:将这一位为 0 的城市集合挂出排列,该排列的作用是将任务编号加上 (模 )。
- 第二次:将这一位为 1 的城市集合挂出同样的排列。
这样,每条边根据其两端城市在这一位的异同,可能被作用 0 次、1 次或 2 次。
3. 如何确定城市分组
代码中的
dfs是关键:dfs(v, u, (t - s + m) % m >> k & 1 ? c : !c, k);这里
(t - s + m) % m是任务需要的变化量(模 )。
>> k & 1检查这个变化量的第 位是否为 1。- 如果第 位为 1,说明这条边在这一轮需要被作用奇数次(1 次)该排列。
- 如果第 位为 0,说明需要被作用偶数次(0 次或 2 次)。
为了让每条边被作用次数符合要求,我们根据这个条件给节点染色:
- 如果
(t - s) >> k & 1 == 1,则子节点颜色与父节点不同(这样该边两端颜色不同,在两次操作中只会被其中一次覆盖 → 总共作用 1 次)。 - 否则,子节点颜色与父节点相同(这样该边两端颜色相同,在两次操作中要么都被覆盖(两端都在 0 集合或都在 1 集合)→ 总共作用 2 次,模 2 意义下等于 0 次变化)。
4. 排列构造
排列 的作用是:
p(j) = (j + (1 << i)) % m + 1即把任务编号加上 (模 ),再加 1 转回 的表示。
这样,作用一次就翻转了第 位(在模 的循环意义下)。
5. 操作次数
对每一位 ,我们做 2 次操作(分别选颜色 0 的集合和颜色 1 的集合)。
共 5 位,所以总共 次操作,符合满分要求。
重要公式
-
任务变化量: [ \Delta = (t - s + m) \bmod m ] 表示需要加多少才能从 到 (模 )。
-
二进制位检查: [ \text{need_flip}_k = (\Delta \gg k) & 1 ] 判断第 位是否需要翻转。
-
排列作用: [ p_k(j) = (j + 2^k) \bmod m + 1 ] 实现第 位的翻转。
- 1
信息
- ID
- 3965
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 4
- 已通过
- 1
- 上传者