FZU2105 线段树 (按位操作)

题目:

Given N integers A={A[0],A[1],...,A[N-1]}. Here we have some operations:

(元素和操作元素 < 16)

Operation 1: AND opn L R

Here opn, L and R are integers.

For L≤i≤R, we do A[i]=A[i] AND opn (here "AND" is bitwise operation).

Operation 2: OR opn L R

Here opn, L and R are integers.

For L≤i≤R, we do A[i]=A[i] OR opn (here "OR" is bitwise operation).

Operation 3: XOR opn L R

Here opn, L and R are integers.

For L≤i≤R, we do A[i]=A[i] XOR opn (here "XOR" is bitwise operation).

Operation 4: SUM L R

We want to know the result of A[L]+A[L+1]+...+A[R].

这个题用线段树解决主要在于两个问题:

1、标记表示:如果简单设置3个布尔类型的变量表示3个操作的标记的话,要考虑标记对自己本身的影响和标记间的相互影响,程序会反而复杂,而如果用一个数组记录区间内所有数中每一位(二进制位,共4位)的1的个数,则可以省略and和or两个标记以及sum变量,空间节省不少,程序也简单很多(主要是考虑标记下传的时候方便一些)

说明:

区间的和可以表示为,每一位上1的总个数 * 对应位的权值(1, 2, 4, 8)的和

and操作和or操作都是把当前节点的1的个数置0或者赋为cnt(区间内数的个数),同时又可以通过判断节点的1的个数是否为0或者cnt来表示是否有标记(and或者or,但已不重要,最终只关心1的个数)

2:pushDown()函数的设计:pushDown()函数是区间修改的核心,作用是线段树标记下传过程中维护标记以及节点的它信息,使线段树时时刻刻保持“正确有序”的姿态, 具体参见代码:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <cmath>
  5 #include <algorithm>
  6 #define mem0(a) memset(a, 0, sizeof(a))
  7 #define lson l, m, rt << 1
  8 #define rson m + 1, r, rt << 1 | 1
  9 #define lr rt << 1
 10 #define rr rt << 1 | 1
 11 using namespace std;
 12 #define LL __int64
 13 struct seg_tree{
 14         int mark;// xor operations‘ mark
 15         int cnt[4];//four digits‘ count(used for other operations)
 16 }tree[1000010 << 2];
 17
 18 void pushUp(int rt)
 19 {
 20         for(int i = 0; i < 4; i++) {
 21                 tree[rt].cnt[i] = tree[rt << 1].cnt[i] + tree[rt << 1 | 1].cnt[i];
 22         }
 23 }
 24
 25 void pushDown(int rt, int cnt)
 26 {
 27         seg_tree &t = tree[rt], &tl = tree[rt << 1], &tr = tree[rt << 1 | 1];
 28         int lc = cnt - (cnt >> 1), rc = cnt >> 1;
 29         for(int i = 0; i < 4; i++) {                             // 按位处理
 30                 if(t.cnt[i] == cnt || t.cnt[i] == 0) {           // 是否全部是0或1
 31                         tl.mark &= (~(1 << i));                  // 左右儿子的标记清0,因为此时以前xor标记不起作用了
 32                         tr.mark &= (~(1 << i));
 33                         tl.cnt[i] = t.cnt[i] > 0 ? lc : 0;      // 儿子的1的个数与当前对应
 34                         tr.cnt[i] = t.cnt[i] > 0 ? rc : 0;
 35                         t.mark &= (~(1 << i));                  // 当前的xor标记清0, 因为子孙的1的个数与当前对应, 当前标记不对当前起作用,
 36                                                                 //那么也不会对子孙起作用
 37                 }
 38                 if(t.mark & (1 << i)) {
 39                         tl.cnt[i] = lc - tl.cnt[i];
 40                         tr.cnt[i] = rc - tr.cnt[i];
 41                         tl.mark ^= (1 << i);
 42                         tr.mark ^= (1 << i);
 43                         t.mark &= (~(1 << i));
 44                 }
 45         }
 46 }
 47
 48 void work(int x, int k, int rt, int cnt)
 49 {
 50         for(int i = 0; i < 4; i++) {
 51                 if(x & (1 << i)) {
 52                         if(k == 0) tree[rt].cnt[i] = cnt - tree[rt].cnt[i], tree[rt].mark ^= (1 << i);
 53                         if(k == 1) tree[rt].cnt[i] = cnt, tree[rt].mark &= (~(1 << i));
 54                 }
 55                 else {
 56                         if(k == 2) tree[rt].cnt[i] = 0, tree[rt].mark &= (~(1 << i));
 57                 }
 58         }
 59 }
 60
 61 int calc(int cnt[])
 62 {
 63         int sum = 0;
 64         for(int i = 0; i < 4; i++) {
 65                 sum += cnt[i] * (1 << i);
 66         }
 67         return sum;
 68 }
 69
 70 void build(int l, int r, int rt)
 71 {
 72         tree[rt].mark = 0;
 73         if(l == r) {
 74                 int x;
 75                 scanf("%d", &x);
 76                 for(int i = 0; i < 4; i++) {
 77                         tree[rt].cnt[i] = ((x & (1 << i)) > 0);
 78                 }
 79                 return;
 80         }
 81         int m = (l + r) >> 1;
 82         build(lson);
 83         build(rson);
 84         pushUp(rt);
 85 }
 86 int query(int L, int R, int l, int r, int rt)
 87 {
 88         if(L <= l && r <= R) {
 89                 return calc(tree[rt].cnt);
 90         }
 91         int m = (l + r) >> 1, res = 0;
 92         pushDown(rt, r - l + 1);
 93         if(L <= m) res += query(L, R, lson);
 94         if(R > m) res += query(L, R, rson);
 95         return res;
 96 }
 97
 98 void update(int L, int R, int x, int k, int l, int r, int rt)
 99 {
100         if(L <= l && r <= R) {
101                 work(x, k, rt, r - l + 1);
102                 return;
103         }
104         int m = (l + r) >> 1;
105         pushDown(rt, r - l + 1);
106         if(L <= m) update(L, R, x, k, lson);
107         if(R > m) update(L, R, x, k, rson);
108         pushUp(rt);
109 }
110
111 int main(){
112         //freopen("input.txt", "r", stdin);
113         int T;
114         cin >> T;
115         while(T--) {
116                 int n, m;
117                 cin >> n >> m;
118                 build(1, n, 1);
119                 for(int i = 1; i <= m; i++) {
120                         int a, b;
121                         char s[10];
122                         scanf("%s%d%d", s, &a, &b);
123                         char ch = s[0];
124                         if(s[0] == ‘S‘) printf("%d\n", query(a + 1, b + 1, 1, n, 1));
125                         else {
126                                 int c;
127                                 scanf("%d", &c);
128                                 if(ch == ‘X‘) update(b + 1, c + 1, a, 0, 1, n, 1);
129                                 if(ch == ‘O‘) update(b + 1, c + 1, a, 1, 1, n, 1);
130                                 if(ch == ‘A‘) update(b + 1, c + 1, a, 2, 1, n, 1);
131                         }
132                 }
133         }
134 }

时间: 2024-10-24 11:28:45

FZU2105 线段树 (按位操作)的相关文章

FZU2105 Digits Count(经典 线段树)

Digits Count Time Limit:10000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Description Given N integers A={A[0],A[1],...,A[N-1]}. Here we have some operations: Operation 1: AND opn L R Here opn, L and R are integers. Fo

FZU2105 Digits Count(按位建线段树)题解

题意: 给出区间与.或.异或\(x\)操作,还有询问区间和. 思路: 因为数比较小,我们给每一位建线段树,这样每次只要更新对应位的答案. 与\(0\)和或\(1\)相当于重置区间,异或\(1\)相当于翻转区间,那么设出两个\(lazy\)搞一下.注意父区间\(pushdown\)重置标记时,子区间的翻转标记要清空. 代码: #include <map> #include <set> #include <queue> #include <cmath> #inc

Codeforces Round #149 (Div. 2) E. XOR on Segment (线段树成段更新+二进制)

题目链接:http://codeforces.com/problemset/problem/242/E 给你n个数,m个操作,操作1是查询l到r之间的和,操作2是将l到r之间的每个数xor与x. 这题是线段树成段更新,但是不能直接更新,不然只能一个数一个数更新.这样只能把每个数存到一个数组中,长度大概是20吧,然后模拟二进制的位操作.仔细一点就行了. 1 #include <iostream> 2 #include <cstdio> 3 #include <cmath>

HDU5023 A Corrupt Mayor&#39;s Performance Art 【线段树】

A Corrupt Mayor's Performance Art Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 100000/100000 K (Java/Others) Total Submission(s): 507    Accepted Submission(s): 203 Problem Description Corrupt governors always find ways to get dirty money.

线段树题目总结

一.单点更新 1.hdu1166 敌兵布阵:有N个兵营,每个兵营都给出了人数ai(下标从1开始),有四种命令,(1)"Addij",表示第i个营地增加j人.(2)"Sub i j",表示第i个营地减少j人.(3)"Query ij",查询第i个营地到第j个营地的总人数.(4)"End",表示命令结束.解题报告Here. 2.hdu1754 I Hate It:给你N个数,M个操作,操作分两类.(1)"QAB"

ACM——敌兵布阵(经典的线段树)

Description Lily 特 别喜欢养花,但是由于她的花特别多,所以照料这些花就变得不太容易.她把她的花依次排成一行,每盆花都有一个美观值.如果Lily把某盆花照料的好的话, 这盆花的美观值就会上升,如果照料的不好的话,这盆花的美观值就会下降.有时,Lily想知道某段连续的花的美观值之和是多少,但是,Lily的算术不是 很好,你能快速地告诉她结果吗? Input 第一行一个整数T,表示有T组测试数据. 每组测试数据的第一行为一个正整数N(N<=50000),表示Lily有N盆花.接下来有

poj 2777 Count Color (线段树区间更新)

Count Color Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 37647   Accepted: 11315 Description Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem.

线段树总结 (转载 里面有扫描线类 还有NotOnlySuccess线段树大神的地址)

转载自:http://blog.csdn.net/shiqi_614/article/details/8228102 之前做了些线段树相关的题目,开学一段时间后,想着把它整理下,完成了大牛NotOnlySuccess的博文“完全版线段树”里的大部分题目,其博文地址Here,然后也加入了自己做过的一些题目.整理时,更新了之前的代码风格,不过旧的代码仍然保留着. 同样分成四类,不好归到前四类的都分到了其他.树状数组能做,线段树都能做(如果是内存限制例外),所以也有些树状数组的题目,会标示出来,并且放

线段树,树状数组(单点操作)

考虑一个无限完整的二叉搜索树(参见下图),节点中的数字是1,2,3,....在根节点为X的子树中,我们可以通过重复获得该子树中的最小数量沿着左边的节点往下走,直到最后一层,我们也可以通过沿着右边的节点找到最大数量.现在给出一些查询,"根节点为X的子树中的最小和最大数目是多少?" 请尝试找到有关查询的答案.  输入 在输入中,第一行包含一个整数N,表示查询的数量.在接下来的N行中,每行都包含一个代表根号为X(1 <= X <= 2 31 - 1)的子树的数字 . 产量 共有N