加载中...

cf div2 1077 CDE

C. Restricted Sorting

显然 \(k\) 是可以二分的,我们考虑对于一个固定的 \(k\),如何 check。

考虑可交换条件 \(|a_{i}-a_{j}| \geq k\):显然,最大值与最小值是最大概率可交换的。而其他一对元素的交换,可以间接地利用两个最值做到。因此,对于每个最终未在排序后位置的元素(已在对应位置上的元素不用看),我们只需要看它是否能和最大值或最小值交换就行了,check 复杂度 \(O(n)\)这个思路类似于虚点:虚点就是两个最值,实点是每个元素,边代表可交换性。对于检查任意一对实点是否连通,只需要分别检查它们与虚点是否连通即可

code

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|\) 的最小值为:

\[min(dp_{0,-1/0/1,-1/0/1}) \]

找到相应的 \(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\) 的值。

code1_迭代
code1_记忆化搜索

2

一个正确性显然的暴力做法

贪心做法实在是想不到,看了眼题解评论区中的一位大佬的评论才明白的:

pZfyMKP.png

首先检查 \(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\) 种情况:

  1. \(p\) 增大,\(q\) 增大:为了让 \(p\)\(x\)\(q\)\(y\) 的值更接近,显然此时我们要尽可能地减小 \(p, q\)。而我们只能改变更低的位(因为正在枚举的就是最高可改变位),显然,将 \(p\)\(q\) 的这些低位均置为 \(0\),会满足低位上没有共同的 \(1\),且是最优解(\(|x-p|+|y-q|\) 尽可能小)。

  2. \(p\) 增大,\(q\) 减小:此时我们需要尽可能地减小 \(p\),增大 \(q\)。此时我们将 \(p\) 的低位置为全 \(0\)\(q\) 的低位置为全 \(1\),是最优解,且仍满足低位上没有共同的 \(1\)

  3. \(p\) 减小,\(q\) 增大:与情况 2 类似,低位 \(p\) 置全 \(1\),低位 \(q\) 置全 \(0\)

  4. \(p\) 减小,\(q\) 减小:这时仍沿用上面的思路就不行了,因为这时的情况是低位 \(p,q\) 均置全 \(1\),不再满足低位上没有共同的 \(1\) 这个必要性质。因此我们需要单独对这种情况作处理。

情况 \(4\) 的特殊处理:此时对于 \(p, q\),我们想让它们均增大,但又不能让每一位上均是 \(1\),而增大量越多越好,显然我们可以让 \(p,q\) 中改变位更高的数的所有低位置为全 \(1\),另一个低位置为全 \(0\)。此时增大量尽可能地大,是最优解(证明自己体会一下吧。。。)。

按照上述思路模拟即可,复杂度 \(O(32^{2})\)

code2

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。

code

posted @ 2026-01-30 14:13  小橘奏  阅读(90)  评论(0)    收藏  举报