cf div2 1077 CDE
C. Restricted Sorting
显然 \(k\) 是可以二分的,我们考虑对于一个固定的 \(k\),如何 check。
考虑可交换条件 \(|a_{i}-a_{j}| \geq k\):显然,最大值与最小值是最大概率可交换的。而其他一对元素的交换,可以间接地利用两个最值做到。因此,对于每个最终未在排序后位置的元素(已在对应位置上的元素不用看),我们只需要看它是否能和最大值或最小值交换就行了,check 复杂度 \(O(n)\)。这个思路类似于虚点:虚点就是两个最值,实点是每个元素,边代表可交换性。对于检查任意一对实点是否连通,只需要分别检查它们与虚点是否连通即可。
D. Shortest Statement Ever
这里给出 \(2\) 种做法。
1
\(dp\) 做法
考虑按照二进制高位到低位进行数位 \(dp\),并在 \(dp\) 过程中记录每一步转移的路径,以还原方案。
状态定义
\(dp_{bit,cp,cq}\):只考虑二进制高 \(bit\) 位的前缀(从第 \(30\) 位开始),\(p\) 已形成的前缀与 \(x\) 相应前缀 的相对大小关系为 \(cp\)(\(-1\) 表示 \(p < x\),\(0\) 表示 \(p=x\),\(1\) 表示 \(p > x\));\(q\) 已形成的前缀与 \(y\) 相应前缀 的相对大小关系为 \(cq\)(同上),\(|x-p|+|y-q|\) 的最小值。
则 \(|x-p|+|y-q|\) 的最小值为:
找到相应的 \(p, q\),按照记录的转移路径一步步回溯即可。
之所以设置 \(cp,cq\) 这样的状态,是因为在决策当前位 \(p, q\) 填什么数字时,需要明确当前已构建前缀与 \(x,y\) 对应前缀的相对大小关系,才能进一步清楚 \(x\) 与 \(p\)(\(y\) 与 \(q\))是更接近了还是更远离了,进而决定变化量对贡献是增加还是减少。
状态转移
即决策当前二进制位上 \(p, q\) 填什么数字。
- 首先,由题目给出的硬性要求 \(p\space \&\space q = 0\) ,对于每个二进制位 \(p\),\(q\) 不能都填 \(1\)。因此只需要枚举 \((0,0), (0, 1),(1,0)\) 三种情况即可。
- 只考虑 \(cp \rightarrow next\_cp\) 的转移(\(cq \rightarrow next\_cq\) 同理):
- \(cp = 0\):也就意味着已经填过的高位前缀上的每一位都是与 \(x\) 相同的。那么 \(p\) 与 \(x\) 在这一位上的二进制数字大小关系就是 \(next\_cp\) 的值。
- \(cp \neq 0\):由于高位权重更高,\(p\) 与 \(x\) 在这一位上的相对大小无法改变形成的新前缀的相对大小,因此这时 \(next\_cp = cp\)。
- 转移形成的贡献的加减需要根据 \(cp,cq\) 进一步决定。
按照上面的想法作状态转移即可,并记录状态转移路径,最后从最优答案一步步回溯,进而得到该情况下 \(p, q\) 的值。
2
一个正确性显然的暴力做法
贪心做法实在是想不到,看了眼题解评论区中的一位大佬的评论才明白的:
首先检查 \(p=x,q=y\) 是否可行,即 \(x\space\&\space y = 0\)。
若不可行,我们仍先预设置 \(p=x,q=y\),显然我们需要改变 \(p\) 或 \(q\) 的值。
既然值有所改变,那么对每个数就必然存在改变的二进制最高位是哪一位(该位上的数字与原数字不同),而二进制下总共才 \(31\) 位,因此我们考虑暴力枚举 \(p\) 与 \(x\),\(q\) 与 \(y\) 不同的最高二进制位(当然也有可能不改变,需要额外枚举一个不改变的情况)。 对于 \(p\) 和 \(q\),我们只对改变的那一位作翻转,则 \(p\) 和 \(q\) 的值要么增大(\(0 \rightarrow 1\)),要么减小(\(1 \rightarrow 0\))。
考虑所有的 \(4\) 种情况:
-
\(p\) 增大,\(q\) 增大:为了让 \(p\) 与 \(x\),\(q\) 与 \(y\) 的值更接近,显然此时我们要尽可能地减小 \(p, q\)。而我们只能改变更低的位(因为正在枚举的就是最高可改变位),显然,将 \(p\) 与 \(q\) 的这些低位均置为 \(0\),会满足低位上没有共同的 \(1\),且是最优解(\(|x-p|+|y-q|\) 尽可能小)。
-
\(p\) 增大,\(q\) 减小:此时我们需要尽可能地减小 \(p\),增大 \(q\)。此时我们将 \(p\) 的低位置为全 \(0\),\(q\) 的低位置为全 \(1\),是最优解,且仍满足低位上没有共同的 \(1\)。
-
\(p\) 减小,\(q\) 增大:与情况 2 类似,低位 \(p\) 置全 \(1\),低位 \(q\) 置全 \(0\)。
-
\(p\) 减小,\(q\) 减小:这时仍沿用上面的思路就不行了,因为这时的情况是低位 \(p,q\) 均置全 \(1\),不再满足低位上没有共同的 \(1\) 这个必要性质。因此我们需要单独对这种情况作处理。
情况 \(4\) 的特殊处理:此时对于 \(p, q\),我们想让它们均增大,但又不能让每一位上均是 \(1\),而增大量越多越好,显然我们可以让 \(p,q\) 中改变位更高的数的所有低位置为全 \(1\),另一个低位置为全 \(0\)。此时增大量尽可能地大,是最优解(证明自己体会一下吧。。。)。
按照上述思路模拟即可,复杂度 \(O(32^{2})\)。
E. Jerry and Tom
只需要证明一件事情:只要移动,Jerry 和 Tom 一定均选择可以到达最远点的那条边。由于 Jerry 获胜必须到达点 \(n\),\(\forall (u,v),u<v\),所以 Jerry 必须尽快地走;而由于整个图的特殊性(\(\forall (u_{i},v_{i})、(u_{j},v_{j}),u_{i}<u_{j}<v_{i}<v_{j}\) 的情况不可能出现),使得类似 dp 的策略失效,想要尽可能地快就必须每次贪心选择当前可以到达的最远点移动。虽然 Tom 的移动比较灵活,但大体的移动策略上类似于 Jerry:都是要尽快地到达点 \(n\),但是一旦发现在某次移动后确保可以截断 Jerry,以后就可以一直停止,直到 Jerry 送上门就行了。因此,到达任意某个点时,下一步走哪条边是唯一确定的。于是,整个图可以变成一棵树(每个点唯一的下一步到达点作为父亲,根节点为 \(n\))。
设 Jerry 的起点为 \(x\),Tom 的起点为 \(y\)。考虑 \(dep_{x}\) 与 \(dep_{y}\) 之间的关系:
- \(dep_{x} \ge dep_{y}\):Tom 一定会在 \(lca(x, y)\) 处抓到 Jerry,此时 \(f(x, y)\) = \(dep_{y} - dep_{lca(x, y)}\);
- \(dep_{x} < dep_{y}\):Jerry 一定比 Tom 的行动快,最后先一步到达点 \(n\)。此时 \(f(x, y) = 0\)。
因此我们有了一份 \(O(n^{2}\log n)\) 的暴力代码:
ll ans = 0;
for(int i = 1; i <= n; i ++){ // Jerry
for(int j = 1; j <= n; j ++){ // Tom
if(i == j) continue;
int l = lca(i, j);
if(dep[i] >= dep[j]){
ans += dep[j] - dep[l];
}
}
}
cout << ans << "\n";
可以通过每次计算某个固定的 \(lca\) 的贡献来做到线性计数,进而转化为每次计算某棵子树的贡献,可以利用树上启发式合并来做;精心设计维护的信息,并利用树状数组/线段树加速计算就可以了。设计信息的方式很多,这里就不多赘述了,具体实现见 code。


浙公网安备 33010602011771号