【bzoj4888】: [Tjoi2017]异或和 BIT-乱搞

时间:2023-03-08 20:24:33
【bzoj4888】: [Tjoi2017]异或和 BIT-乱搞

【bzoj4888】: [Tjoi2017]异或和

题目大意:给定一个序列,求这个序列所有的连续和的异或值。(n<=1e5 ai<=1e6)

想了各种奇怪的方法就是不会做啊啊啊。。

Orz 参考了一下http://www.cnblogs.com/xiejiadong/p/6815269.html才会做的。。

恩因为不超过20位。。所以可以考虑枚举所有连续和在第i位1的个数

预处理完前缀和就可以O(1)求出任意一个连续和。。

当考虑到第i位的时候,把前缀和扫一遍

当扫到第x个前缀和sum[x],那么所有满足 第y个前缀和sum[y](y<x)的第i位=sum[x] 的第i位 且 sum[x]的i后面的位<sum[y]的i后面的位

                  或 第y个前缀和sum[y](y<x)的第i位!=sum[x] 的第i位 且 sum[x]的i后面的位>sum[y]的i后面的位

的sum[x]-sum[y]一定是一个对第i位有贡献的连续和。。

理解了半天。。手玩了几个数才明白。。在扫一遍的时候用BIT维护一下就好了。。

 /* http://www.cnblogs.com/karl07/ */
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std; #define lowbit(x) (x&-x)
const int N=1e5+;
const int MX=1e6+1e5;
int n,m;
int a[N],b[][MX],as[]; int bt(int x,int y){
return (x>>(y-))&;
} void PR(int x){
if (x) PR(x>>);
printf("%d",x&);
}
void pr(int x){
PR (x);
puts("");
} int query(int p1,int x){
int ans=;
while (x){
ans+=b[p1][x];
x-=lowbit(x);
}
return ans;
} void modify(int p1,int x){
while (x<=MX){
b[p1][x]++;
x+=lowbit(x);
}
} int main(){
scanf("%d",&n);
for (int i=;i<=n;i++){
scanf("%d",&a[i]);
a[i]+=a[i-];
}
for (int i=,ba=;i<=;i++){
memset(b,,sizeof(b));
for (int j=;j<=n;j++){
as[i]+=query(bt(a[j],i),ba+)-query(bt(a[j],i),a[j]%ba+)+query(!bt(a[j],i),a[j]%ba+)+bt(a[j],i);
modify(bt(a[j],i),a[j]%ba+);
}
ba=ba*;
}
int ans=;
for (int i=,ba=;i<=;i++,ba*=) ans+=(as[i]&)*ba;
printf("%d\n",ans);
return ;
}