【Luogu】P3760异或和(权值树状数组)
再次声明以后我见到位运算一定第一时间想把它拆成每一位算
本题就是有个前缀和sum[],然后让你求每一位有多少对i,j满足sum[i]-sum[j]在那一位上是1
考虑怎样才能减出1来
如果sum[i]在这一位是1的话,那么就需要j是0且sum[i]前面的数小于sum[j]前面的数,这样不至于一减减退位了,把sum[i]这一位的1减没了
如果是0同理
考虑用权值树状数组维护。
#include<cstdio> #include<algorithm> #include<cstring> #include<cctype> #include<cstdlib> #define maxn 1000010 using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } inline int low(int x){ return x&(-x); } int tot; int sum[maxn]; struct Tree{ int tree[maxn]; void clear(){ memset(tree,0,sizeof(tree)); } inline void add(int pos){ while(pos<=tot){ tree[pos]++; pos+=low(pos); } } inline int ask(int pos){ int ans=0; while(pos>0){ ans+=tree[pos]; pos-=low(pos); } return ans; } }zero,one; int main(){ int n=read(); for(int i=1;i<=n;++i) sum[i]=sum[i-1]+read(); tot=sum[n]+1; int ans=0; for(int i=0;(1<<i)<=tot;++i){ zero.clear();one.clear(); int cnt=0; zero.add(1); for(int j=1;j<=n;++j){ int now=(sum[j]%(1<<i))+1; if(((sum[j]>>i)&1)==1){ cnt+=zero.ask(now); cnt+=one.ask(tot)-one.ask(now); one.add(now); } else{ cnt+=zero.ask(tot)-zero.ask(now); cnt+=one.ask(now); zero.add(now); } } //printf("%d %d\n",i,cnt); if(cnt&1) ans+=1<<i; } printf("%d",ans); return 0; }