P3760 [TJOI2017]异或和
思路
二进制肯定拆位考虑
然后思考每个位上多少个连续和的贡献次数
前缀和一下,假设求得第k位,问题变成了求解对一个i,有多少个s[i]-s[j]的对应位是1
因为有减法,所以考虑一下借位的思想
假设这个数对应位是1,就是求有多少个数对应位是0且右侧的二进制位的大小小于等于这个数右侧的二进制位(不借掉前面的1)和多少个数对应位是1且右侧的二进制位的大小大于这个数右侧的二进制位(借掉前面的1),对应位是0则相反,如果有奇数个,就对答案的对应位产生贡献,否则不产生贡献
然后总和小于1e6,上权值树状数组即可
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int MAXW;
int s[100100],num[100100],n;
struct BIT{
int w[1000100];
int lowbit(int x){
return x&(-x);
}
void add(int pos){
pos++;
while(pos<=MAXW){
w[pos]++;
pos+=lowbit(pos);
}
}
int query(int pos){
pos++;
int ans=0;
while(pos){
ans+=w[pos];
pos-=lowbit(pos);
}
return ans;
}
void init(void){
memset(w,0,sizeof(w));
}
}B0,B1;
int main(){
int ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&s[i]),s[i]+=s[i-1],MAXW=max(MAXW,s[i]);
for(int i=0;i<21;i++){
B0.init();B1.init();
B0.add(0);
for(int j=1;j<=n;j++){
int mid=0;
if((s[j]>>i)&1){
mid=B1.query(MAXW-1)-B1.query(num[j])+B0.query(num[j]);
B1.add(num[j]);
num[j]|=(1<<i);
}
else{
mid=B0.query(MAXW-1)-B0.query(num[j])+B1.query(num[j]);
B0.add(num[j]);
}
if(mid%2)
ans^=(1<<i);
}
}
printf("%d\n",ans);
return 0;
}