原题传送门

本题大意:给定n个数字和m个操作,操作共有两种,第一种是求解区间l到r上元素的和,第二种是将区间l到r的元素都异或一个x,作为某个位置的新值。

很容易想到线段树维护区间和,但是我们发现,在区间更新的时候,并没有办法更新,因为我们并不能通过一个区间的和还有我们要异或的值得到这个区间新的异或和,那么我们考虑其他方法。

下面讲述的这个方法,对于值域不大于1 << 20的数据,我们选择用20个数组保存某一个区间k中第 i 位的1的个数,为什么要这么搞呢?

对于第i位,如果区间k的第 i 位原本有s[ i ][ k ]个1,那么对这个区间所有数字都异或一个数字x,我们只需要判断 x 的每一位,如果存在第 i 位为1,那么s[ i ][ k ] = 区间长度 - s[ i ][ k ]。如果第 i 位为0,那么我们忽略这一位。

why ????

0 xor 1 = 1....   0 xor 0 = 0  也就是说o异或任何值等于任何值。

1 xor 0 = 1.......1 xor 1 = 0 也就是说与1异或之后值变为相反数。所以区间内1的个数就变为了区间中零的个数。

这样我们就可以轻松统计某个区间某一位上有多少个1。query操作相信应该都是明了的。

那么懒惰标记如何处理呢??

当然是用一个数组lazy_lable记录区间k应该往下推的值,如果该值还未下推又来了另一个值需要改变,那么就将这两个值异或起来,多者不限,也就是说只需要一个一维数组来记录每个区间的lazy值就行了。

ok相信大家都懂了,本来讲之前本人还不怎么懂。。。。。结果讲完就懂了。。。舒服

 #include <cstdio>
#include <cstring>
#include <algorithm>
#define mid ((l + r) >> 1)
using namespace std; typedef long long ll; const int maxn = + ; ll segment_tree[][maxn << ], lazy_lable[maxn << ]; int n, m, x; void push_up(int k) {
for(int i = ; i < ; i ++) {
segment_tree[i][k] = segment_tree[i][k << ] + segment_tree[i][k << | ];
}
} void push_down(int k, int son) {
if(lazy_lable[k]) {
lazy_lable[k << ] ^= lazy_lable[k];
lazy_lable[k << | ] ^= lazy_lable[k];
for(int i = ; i < ; i ++) {
if((lazy_lable[k] >> i) & ) {
segment_tree[i][k << ] = son - (son >> ) - segment_tree[i][k << ];
segment_tree[i][k << | ] = (son >> ) - segment_tree[i][k << | ];
}
}
lazy_lable[k] = ;
}
} void build(int l, int r, int k) {
if(l == r) {
scanf("%d", &x);
for(int i = ; i < ; i ++) {
if((x >> i) & )
segment_tree[i][k] = ;
}
return;
}
build(l, mid, k << );
build(mid + , r, k << | );
push_up(k);
} void update(int L, int R, int x, int l, int r, int k) {
if(L <= l && R >= r) {
lazy_lable[k] ^= x;
for(int i = ; i < ; i ++) {
if((x >> i) & ) {
segment_tree[i][k] = r - l + - segment_tree[i][k];
}
}
return;
}
push_down(k, r - l + );
if(L <= mid) update(L, R, x, l, mid, k << );
if(R > mid) update(L, R, x, mid + , r, k << | );
push_up(k);
} ll query(int L, int R, int l, int r, int k) {
if(L <= l && R >= r) {
ll cnt = ;
for(int i = ; i < ; i ++) {
cnt += segment_tree[i][k] << i;
}
return cnt;
}
push_down(k, r - l + );
ll ans = ;
if(L <= mid) ans += query(L, R, l, mid, k << );
if(R > mid) ans += query(L, R, mid + , r, k << | );
return ans;
} int main() {
int op, l, r, x;
scanf("%d", &n);
build(, n, );
scanf("%d", &m);
while(m --) {
scanf("%d", &op);
if(op == ) {
scanf("%d %d", &l, &r);
printf("%lld\n", query(l, r, , n, ));
} else {
scanf("%d %d %d", &l, &r, &x);
update(l, r, x, , n, );
}
}
return ;
}

最新文章

  1. google jquery用不了啦,你准备好了吗
  2. 自定义有监听器的ScrollView
  3. my_mosaic
  4. Android成长日记-Fragment的生命周期与Activity通信
  5. Ubuntu 下为 Idea 创建启动图标.
  6. ServletContextListener 启动SPRING加载数据到缓存的应用
  7. javascript 中caller,callee,call,apply 的概念[转载]
  8. Fragment与Activity交互(使用Handler)
  9. Pomelo的Router
  10. 高效测试用例组织算法pairwise之Python实现
  11. base(function strchr)
  12. [Union]C++中Union学习笔记
  13. 当yum安装出现Error: Package: glibc-headers .....时
  14. zxing开源库的基本使用
  15. MySQL api
  16. Easy and cheap cluster building on AWS backup
  17. telnet命令的使用方法
  18. linux c++下载http文件并显示进度&lt;转&gt;
  19. JS 事件冒泡、捕获。学习记录
  20. 深入理解javascript原型和闭包_____全部

热门文章

  1. js callback回调的一种写法
  2. Nowcoder Monotonic Matrix ( Lindstr&#246;m–Gessel–Viennot lemma 定理 )
  3. 51 Nod 1073 约瑟夫环
  4. 暑假集训 #2 div1 I - Lada Priora 精度处理
  5. 序列模式挖掘--SPADE算法
  6. BZOJ 1488 Luogu P4727 [HNOI2009]图的同构 (Burnside引理、组合计数)
  7. Android_(游戏)打飞机04:绘画敌机、添加子弹
  8. MQTT通配符
  9. leetcode 230二叉搜索树中第k小的元素
  10. 【转载】解决jquery-1.10.2.min.map 404 Not Found错误