248.小X的区间或值和 | 豆包MarsCode AI刷题

83 阅读2分钟

问题描述

小X有一个数组,她定义数组的权值为数组中任选两个数的按位或的值之和。具体来说,对于数组中的每个连续子数组,我们可以计算所有可能的两个元素的按位或值之和,并将这些值相加。小C想知道该数组中所有可能的连续子数组的权值和是多少,最后结果对10^9 + 7取模。

解题思路

转换成二进制计算;

如:

2——010;3——011;4——100

2|3=011

2|4=110

可知2|3+2|4比3+4多了一个010;

推广: 对于ai,ai或前i个值的和必须保证ai二进制数位上为1的位置前i-1个数有i-1个,其他位置累加即可;

定义数组z[32]记录前i个数各二进制为1的数量;

对于计算所有子数组,我们可以通过计算以a[i]结尾的区间或值和累加得到;

代码样例

z[i]记录二进制第i位上为1的数量;

zz[i]为2^i;

pri为以a[i-1]结尾的所有区间的和;

sum为以a[i]结尾的区间或值和;

ans记录答案;

#include <iostream>
#include <vector>
using namespace std;

int solution(int n, vector<int> a) {
    int mod=1e9+7;
    int z[32]={0},zz[32]={0};
    zz[0]=1;
    for(int i=1;i<32;i++){    //预处理zz[i]节省时间
        zz[i]=zz[i-1]*2;
    }
    int ans=0,pri=0,sum=0;
    for(int i=0;i<n;i++){
        sum=(sum+pri)%mod;  //加上以a[i-1]结尾的区间和,更新sum;
        int x=a[i],k=0; //k记录二进制位置;
        while(x){
        //寻找a[i]二进制为1的位置k;
            if(x&1){
            //下标i前有i个数,以a[i]结尾有i个长度大于1的区间,所以a[i]二进制上为1的位置应该有i*(i-1)/2个数;
                if(z[k]<i*(i+1)/2)sum=(sum+(zz[k]*(i*(i+1)/2-z[k]))%mod)%mod;
                z[k]=(z[k]+i+1)%mod; //+i是因为前面有i个区间,+1是因为新增以a[i]开始的区间;
            }
            k++;
            x>>=1;
        }
        ans=(ans+sum)%mod;  //累加以a[i]为结尾的区间或值和
        pri=(pri+(a[i]*(i+1))%mod)%mod;//更新(i+1)个区间的和
    }
    // write code here
    return ans;  //返回ans,即所有连续子数组的权值和。
}

int main() {
    cout << (solution(4, {2, 3, 1, 2}) == 44) << endl;
    cout << (solution(3, {5, 6, 7}) == 35) << endl;
    cout << (solution(2, {1, 10}) == 11) << endl;
    cout << (solution(5, {1, 2, 4, 8, 16}) == 402) << endl;
    return 0;
}

while(x)的最大复杂度为32;

所以最大时间复杂度为O(32n);