问题描述
小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);