C++程序 使用按位或,在将给定数组拆分成两半并进行K次循环移位后找到数组和

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个查询的答案。

下面是上述方法的实现:

// C++程序,找到数组的两个相等的一半在执行K个右循环移位之后的位或
#include <bits/stdc++.h>
const int MAX = 100005;
using namespace std;
 
//用于存储线段树的数组
int seg[4 * MAX];
 
//建立线段树的函数
void build(int node, int l, int r, int a[])
{
    if (l == r)
        seg[node] = a[l];
 
    else {
        int mid = (l + r) / 2;
 
        build(2 * node, l, mid, a);
        build(2 * node + 1, mid + 1, r, a);
 
        seg[node] = (seg[2 * node]
                     | seg[2 * node + 1]);
    }
}
 
//返回一定范围内元素的OR的函数
int query(int node, int l, int r,
          int start, int end, int a[])
{
    //检查越界条件
    if (l > end or r < start)
        return 0;
 
    if (start <= l and r <= end)
        return seg[node];
 
    //找到范围的中间部分
    int mid = (l + r) / 2;
 
    //递归处理数组中的所有元素
    return ((query(2 * node, l, mid,
                   start, end, a))
            | (query(2 * node + 1, mid + 1,
                     r, start, end, a)));
}
 
//找到OR和的函数
void orsum(int a[], int n, int q, int k[])
{
    //建立线段树的函数
    build(1, 0, n - 1, a);
 
    //处理q个查询的循环
    for (int j = 0; j < q; j++) {
        //有效的循环右移数
        int i = k[j] % (n / 2);
 
        //计算来自分段树的两个数组半部分的OR
 
        // 数组的第二个半部分[n/2-i,n-1-i]的OR
        int sec = query(1, 0, n - 1,
                        n / 2 - i, n - i - 1, a);
 
        // 数组的第一个半部分[n-i,n-1] OR [0,n/2-1-i]
        int first = (query(1, 0, n - 1, 0,
                           n / 2 - 1 - i, a)
                     | query(1, 0, n - 1,
                             n - i, n - 1, a));
 
        int temp = sec + first;
 
        //打印查询的最终答案
        cout << temp << endl;
    }
}
 
//主函数
int main()
{
 
    int a[] = { 7, 44, 19, 86, 65, 39, 75, 101 };
    int n = sizeof(a) / sizeof(a[0]);
 
    int q = 2;
 
    int k[q] = { 4, 2 };
 
    orsum(a, n, q, k);
 
    return 0;
}  

输出:

238
230

时间复杂度: O(N + Q*logN)

辅助空间: O(4*MAX)

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程

C++ 示例