问题 1:
给定序列 \(a_0, a_1, a_2, \cdots, a_n\) 满足 \(n - 1 = 2^{k} (k \geq 0)\) 。定义 \(R_{i}\) 为 \(i\) 的 \(k\) 位的无符号二进制反转。
输出 \(a_{R_{0}}, a_{R_{1}}, a_{R_{2}}, \cdots, a_{R_{n - 1}}\) 。
题解:
首先考虑如何得到 \(R_{i}\) 。对二进制下标使用微扰法,注意到:
\[\begin{aligned} &\lfloor \frac{ R_{\lfloor \frac{i}{2}} \rfloor }{2} \rfloor = R_{i} \odot (2^{n - 1} - 1) \\ \Rightarrow &R_{i} = \lfloor \frac{ R_{\lfloor \frac{i}{2}} \rfloor }{2} \rfloor | ( (i \odot 1) \times 2^{n - 1} ) \end{aligned} \]其次考虑如何让 \(a_0, a_1, a_2, \cdots, a_{n - 1}\) 变成 \(a_{R_{0}}, a_{R_{1}}, a_{R_{2}}, \cdots, a_{R_{n - 1}}\) 。注意到序列分解成了 \(\frac{n - 1}{2}\) 个循环群,每个群为 \((i, R_{i})\) ,只需循环移位一次。\(for \ i \ = 0 \to \frac{n - 1}{2}\) \(swap(a[i], a[R[i]])\) 即实现。
问题 2:
考虑对 \(0, 1, 2, \cdots, n\) 满足 \(n - 1 = 2^{k} (k \geq 0)\) 的数轴二分分治。对分治的每个区间 \(l, r\) 需要使用指针 \(i, j\) 分别指向起点和中点开始并行右移。做出非递归版本的代码实现。
题解:
代码实现如下。
for (int m = 2; m < n; m *= 2) {
for (int l = 0; l + m - 1 < n; l += m) {
for (int i = l, j = l + m / 2; i < l + m / 2; i++, j++) {
}
}
}
- 问题的关键在于先考虑从小到大枚举区间长度。从最小非叶子区间长度 \(m = 2\) ,枚举到根的区间长度 \(n - 1\) ,每次区间长度会乘以 \(2\) 。
- 其次在于枚举每个区间的左端点 \(l\) ,下一个区间的端点为 \(l + m\) ,需要保证右端点不超过 \(n - 1\) 即 \(l + m - 1 < n\) 。
- 最后是对每个区间操作,显然不难是并行指针 \(i, j\) 分别指向 \(l\) 和中点 \(l + m / 2\) ,保证左指针不超过中点或右指针不超过右端点即可。