C++程序 使用按位或,在将给定数组拆分成两半并进行K次循环移位后找到数组和
给定长度为 N (其中 N 为偶数)的数组 A[] ,任务是回答 Q 个独立的查询,每个查询包含一个正整数 K ,表示在数组上执行的循环移位的次数,并通过在划分的数组上执行按位 或 操作找到元素的和。
注意: 每个查询都从原始数组开始。
例子:
输入: A[] = {12, 23, 4, 21, 22, 76}, Q = 1, K = 2
输出: 117
解释:
由于K是2,修改后的数组A[] = {22, 76, 12, 23, 4, 21}。
字节数组的前一半的按位 或 = (22 | 76 | 12) = 94
字节数组的后一半的按位 或 = (21 | 23 | 4) = 23
OR值的总和是94 + 23 = 117
输入: A[] = {7, 44, 19, 86, 65, 39, 75, 101}, Q = 1, K = 4
输出: 238
由于K是4,修改后的数组A[] = {65, 39, 75, 101, 7, 44, 19, 86}。
字节数组的前一半的按位 或 = 111
字节数组的后一半的按位 或 = 127
OR值的总和是111 + 127 = 238
Naive Approach:
要解决上述问题,最简单的方法是将数组的每个元素移动 K % (N / 2) 并遍历数组以为每个查询计算两个部分的OR。但是,该方法不是有效的,因此可以进一步优化。
Efficient Approach:
要优化上述方法,我们可以借助于段树数据结构。
Observation:
- 我们可以观察到,在恰好 N / 2 次右旋之后,数组的两半与原始数组相同。这将有效地将旋转次数减少为 K % (N / 2) 。
- 执行向右循环移位基本上是将数组的最后一个元素移动到前面。因此,对于任何正整数 X 执行 X 次右旋等于将数组的最后X个元素移动到前面。
以下是解决该问题的步骤:
- 为原始数组 A[] 构造一个段树,并分配一个变量,假设为 i = K % (N / 2) 。
- 对于每个查询,我们使用segment tree查询位OR;即从末尾开始的i个元素的按位OR结果 OR 前半部分 (N / 2) – i – 1 个元素的按位OR。
- 然后计算范围内元素的按位OR [(N / 2) – i, N – i – 1] 。
- 将两个结果相加以获取第i个查询的答案。
下面是上述方法的实现:
输出:
时间复杂度: O(N + Q*logN)
辅助空间: O(4*MAX)