正题
题目链接:https://www.luogu.com.cn/problem/P3760
题目大意
给出nnn个数字的一个序列aaa,求它所有区间和的异或和
n≤105,∑ai≤106n\leq 10^5,\sum a_i\leq 10^6n≤105,∑ai≤106
解题思路
开始写了个前缀和+FFT+FFT+FFT发现要卡常然后就换了个方法。
每一个位分开考虑,现在要求有多少个区间和的第kkk位是111。
设sumsumsum为区间和,那么为了方便计算我们要把andandand操作转换一下。就有sum%2k+1∈[2k,2k+1)sum\% 2^{k+1}\in[2^{k},2^{k+1})sum%2k+1∈[2k,2k+1)。
看起来好像更复杂了,其实没有,因为2k2^k2k我们可以直接枚举,那么对于模2k+12^{k+1}2k+1次方意义下在[2k,2k+1)[2^{k},2^{k+1})[2k,2k+1)范围内的区间和就符合要求。
这个就是树状数组的活了
时间复杂度O(nlog2n)O(n\log^2 n)O(nlog2n)(反正是logloglog,∑ai\sum a_i∑ai算和nnn同级得了)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lowbit(x) (x&-x)
using namespace std;
const int N=1<<20;
int n,a[N],t[N],ans,lim;
void Change(int x,int val){x++;while(x<=lim){t[x]+=val;x+=lowbit(x);}return;
}
int Ask(int x){int ans=0;x++;while(x){ans+=t[x];x-=lowbit(x);}return ans;
}
int Query(int l,int r)
{return Ask(r)-Ask(l-1);}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);a[i]+=a[i-1];}for(int lg=1;lg<=20;lg++){lim=(1<<lg);int k=lim>>1,tmp=0;Change(0,1);for(int i=1;i<=n;i++){ int w=a[i]%lim;if(w>=k)tmp+=Query(w+1,lim-1)+Query(0,(w+k)%lim);else tmp+=Query(w+1,w+k);Change(w,1);tmp%=2;}for(int i=1;i<=n;i++)Change(a[i]%lim,-1);if(tmp&1)ans|=k;Change(0,-1);}printf("%d\n",ans);
}