这道题是二进制的改进题目,目的是利用二进制来减少时间复杂度
这类题对数学要求比较高!!!
首先要明确几个定理
1.异或运算(^)是满足交换律和结合律
2.a^b^a=b
3. a^b=c <=> a=c^b
首先!先看看这个问题是如何与二进制扯上关系的!!
目的:求一个区间的异或和
可以先把区间的数都看成二进制数(实质上^运算就是按照二进制算的)
假如:对于二进制的第 i 位数字 如果区间中(即运算过程中)的1的个数为奇数,那么最后也为1
那么,如果是偶数,那么最后也为0 比如 7^5^6 = 4
1 1 1 1 (3个1,1的个数为奇数)
1 0 1 = 0 (2个1,1的个数为偶数)
1 1 0 0 (2个1,1的个数为偶数)
那么对于这个区间如果想计算最后的异或和,那么其实就可以用每一位二进制最后的结果(0或者1)
乘以这一位的权重
那么问题来了!这好像没啥卵用啊
这道题要算长度为l的区间(l为偶数),要查询很多的区间
这样写也肯定要超时啊!!!
别急!先做个预处理,开一个数组num,用来储存前n项异或和
然后别忘了定理 2
如果用 num[ j ]^num[ i ] ( j > i ) 结果会是什么
没错,算出了从i到j的异或和
然后还有
要求区间【l,r】第i位的异或值
即num【r】^num【l】
因为只有异或结果为1才有贡献,即 num【r】^num【l】=1
即表示如果满足上式,该位有贡献值
那么 我们只要找一下有多少个区间有贡献值,记为ans,然后用ans乘以二进制的权重
这样就求出来二进制位的值
那么有多少个区间??
如果l为奇数,那么r必定为偶数
如果l为偶数,那么r必定为奇数
那么只要找一下l前面的奇数偶数的个数,那么不就知道了有多少个区间了???(注意要在题目给定的长度里)
#include<bits/stdc++.h>
#define N 100005
#define P pair<int,int>
using namespace std;
typedef long long ll;
const int M=1e9+7;
int a[N],dp[2][2];
int main()
{
int n,l,r;
scanf("%d%d%d",&n,&l,&r);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
a[i]^=a[i-1];
}
if(l&1)l++;
if(r&1)r--;
if(l>r){
printf("0\n");
return 0;
}
ll ans=0;
for(int i=0;i<32;i++){
ll tmp=0;
memset(dp,0,sizeof(dp));
for(int j=1;j<=n;j++){
if(j>=l)dp[(j-l)&1][((a[j-l]>>i)&1)^1]++;
tmp=(tmp+dp[j&1][(a[j]>>i)&1])%M;
if(j>=r)dp[(j-r)&1][((a[j-r]>>i)&1)^1]--;
}
ans=(ans+tmp*(1LL<<i)%M)%M;
}
printf("%lld\n",(ans+M)%M);
return 0;
}