题意:给定一个序列,要求完成俩个操作
1)查询区间[l,r] 的和
2)区间更新, 区间每一个数都异或一个数x
对每一个查询输出结果
分析:对区间更新和查询这种操作,比较明显的就想到了线段树。区间查询比较简单,关键是区间更新该如何解决。
我们为线段数的每一个节点开一个cnt[20] 的数组,保存该区间内每一个数二进制位上1的个数,cnt[0] 表示对应区间内所有数第一个二进制数上1的个数和,这样,针对每一个异或操作,我们可以这样解决:
for(int i = 0; i < L; ++i) { if(!(v & ( 1 << i) )) continue;//针对异或操作的性质,如果v的第i位为0,则这个区间内,第i位上的1的个数不变;
//若第i位为1,则第i为上1 的个数和0 的个数互换 p[k].cnt[i] = p[k].r-p[k].l + 1 - p[k].cnt[i]; }
这样,在计算区间和时
for(int i = 0; i < L; ++i) ans += ((p[k].cnt[i] * 1ll) << i);
当然,要加速对区间更新的操作,肯定要运用lazy的思想
完整代码
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; const int N = 100000 + 10; const int L = 20; struct Node { int l,r; int c,cnt[L];//cnt[i] 表示区间[l,r]内二进制第i位上1的个数 }p[3*N]; int que[N]; void build(int k,int s,int t) { int kl, kr, mid; p[k].l = s; p[k].r = t;p[k].c = 0; if(s == t) { for(int i = 0; i < L; ++i) { if(que[s] & ( 1 << i )) p[k].cnt[i] = 1; } return ; } mid=(s + t) >> 1; kl = k << 1; kr = kl + 1; build(kl, s, mid); build(kr, mid+1, t); for(int i = 0; i < L; ++i) p[k].cnt[i] = p[kl].cnt[i] + p[kr].cnt[i]; } void Push_Down(int k, int v) { p[k].c ^= v; for(int i = 0; i < L; ++i) { if(!(v & ( 1 << i) )) continue; p[k].cnt[i] = p[k].r-p[k].l + 1 - p[k].cnt[i]; } } void insert(int k,int s,int t,int v) { if(s <= p[k].l&& t >= p[k].r) { Push_Down(k, v); return ; } else { int mid=(p[k].r + p[k].l) >> 1, kl = k << 1, kr = kl + 1; if(p[k].c != 0) { Push_Down(kl, p[k].c); Push_Down(kr, p[k].c); p[k].c = 0; } if(s <= mid) insert(kl, s, t, v); if(t > mid) insert(kr, s, t, v); for(int i = 0; i < L; ++i) p[k].cnt[i] = p[kr].cnt[i] + p[kl].cnt[i]; } } __int64 query(int k,int s,int t) { if(s <= p[k].l && t >= p[k].r) { __int64 ans = 0; for(int i = 0; i < L; ++i) ans += ((p[k].cnt[i] * 1ll) << i); return ans; } int mid=(p[k].r + p[k].l) >> 1,kl = k << 1,kr = kl + 1; if(p[k].c) { Push_Down(kl, p[k].c); Push_Down(kr, p[k].c); p[k].c = 0; } __int64 a = 0,b = 0; if(s <= mid) a = query(kl,s,t); if(t > mid) b = query(kr,s,t); for(int i = 0; i < L; ++i) p[k].cnt[i] = p[kl].cnt[i] + p[kr].cnt[i]; return a + b; } int main() { int i,n,m,a,b,t,k; scanf("%d",&n); for(i = 1; i <= n; ++i) scanf("%d",que + i); build(1,1,n); scanf("%d",&m); while(m--) { scanf("%d",&k); if(k == 1) { scanf("%d %d",&a,&b); printf("%I64d\n",query(1,a,b)); } else { scanf("%d %d %d",&a,&b,&t); if(t != 0) insert(1,a,b,t); } } return 0; }