题目链接:https://www.nowcoder.com/acm/contest/35/B
题目给出N个数字,然后求区间长度在【L,R】内且为偶数的所有子
区间的异或和。
看别人的代码看了好久才明白过来。就是按照二进制位的贡献来进行计算。
对于一个【l,r】的区间,如果其第i个2进制位(从低位往高位数)如果有奇数
个1,则异或后第i个二进制位为1,对答案贡献2^i。为了可以方便的求的
任意区间内所有元素的异或值,我们可以先进行前缀异或,即假如有数组
a,则a[i]代表前i个数字异或后的值,则如果求XOR【L,R】有如下式子:
XOR【L,R】 = a[R] xor a[R-L]. 原因是异或满足交换率,相同的数字异或后
为0,而0异或上任何数都为其本身。
因此求[L,R】区间数字的第i个二进制位的异或值“
XOR(bit i)[L,R] = (bit i) a[R] xor (bit i)a[R-L].
如果欲让其对结果有贡献,则如果a[R]的第i位为1,则a[R-L]的第i位应该为0.
如果a[R]的第i位为0,则a[R-L]的第i为应该为1.
而这个题目需要的区间是偶数长度的区间
因此如果j是奇数,则以j为右边界的满足条件的区间的左边界肯定都是奇数。
如果j是偶数,则以j为右边界的满足条件的区间的左边界肯定都是偶数。
因此为了计算满足以第j个数字为结尾的符合条件的区间其第i位的贡献,我们
需要分奇数和偶数。
因为j分了奇偶,区间异或值的第i位也有0,1之分,状态组合为4种。
AC代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int mod = 1e9+7;
const int maxn = 1e5+10;
typedef long long LL;
int num[maxn];
int cnt[2][2];
int main() {
int N,L,R;
while(~scanf("%d%d%d",&N,&L,&R)) {
num[0] = 0;
for(int i = 1; i <= N; i++) {
scanf("%d",&num[i]);
num[i] = num[i]^num[i-1]; ///求前缀异或
}
///L是奇数,L++
if(L&1) L++;
///R是奇数,R--
if(R&1) R--;
if(R < L) {
printf("0\n");
}
else {
LL sum = 0;
///求第i位的贡献,第i位每有一个位位1,则贡献2^i
for(int i = 0; i < 32; i++) {
int ans = 0;
memset(cnt,0,sizeof(cnt));
for(int j = L; j <= N; j++) {
/**
cnt[0][0]代表,j是偶数,且前J-L个值异或后,其第i个二进制位为0
cnt[0][1]代表,j是偶数,且前J-L个值异或后,其第i个二进制位为1
cnt[1][0]代表,j是奇数,且前J-L个值异或后,其第i个二进制位为0
cnt[1][1]代表,j是奇数,且前J-L个值异或后,其第i个二进制位为1
*/
cnt[j&1][(num[j-L]>>i)&1]++;
ans = (ans + cnt[j&1][!((num[j]>>i)&1)])%mod;
///J>=R的时候,减去多余的部分
if(j >= R) {
cnt[j&1][(num[j-R]>>i)&1]--;
}
}
LL power = 1;
power = (power<<i)%mod; ///第i个二进制权值为2^i
sum = (sum + (ans*power)%mod)%mod;
}
printf("%lld\n",sum);
}
}
return 0;
}