题目:点击打开链接
题意:n个数字,四种操作, 区间AND OR XOR 一个数, 区间求和
数字范围很小,所以可以按位去操作,区间AND和区间OR其实就可以理解为区间置0或置1,XOR为区间取反,
AND和OR是可以一起操作的,但结合XOR,如果用一个lazy标记是不可行的,所以开两个lazy标记,一个是有没有区间置数,第二个是有没有区间取反。
区间置数:无论有没有取反标记,都应该置为相应数,并且要把取反标记去掉
区间取反:因为pushdown里当两个标记同时出现时,是不考虑取反标记的,所以我们可以在更新时,判断一下有没有区间置数标记,有的话只取反一下置数标记,就不用向下传取反标记了,如果没有则要向下传取反标记
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn = 1e6 + 10;
int seg[maxn * 4][4], lazy[2][maxn * 4][4];
void pushup(int node, int i){
seg[node][i] = seg[node << 1][i] + seg[node << 1 | 1][i];
return ;
}
void pushdown(int node, int l, int r, int i){
int mid = (l + r) >> 1;
if(lazy[0][node][i] != -1){
seg[node << 1][i] = lazy[0][node][i] * (mid - l + 1);
seg[node << 1 | 1][i] = lazy[0][node][i] * (r - mid);
lazy[0][node << 1][i] = lazy[0][node << 1 | 1][i] = lazy[0][node][i];
lazy[0][node][i] = -1, lazy[1][node][i] = 0;
}
else if(lazy[1][node][i]){
seg[node << 1][i] = mid - l + 1 - seg[node << 1][i];
seg[node << 1 | 1][i] = r - mid - seg[node << 1 | 1][i];
lazy[1][node << 1][i] ^= lazy[1][node][i];
if(lazy[0][node << 1][i] != -1){
lazy[0][node << 1][i] ^= 1, lazy[1][node << 1][i] = 0;
}
lazy[1][node << 1 | 1][i] ^= lazy[1][node][i];
if(lazy[0][node << 1 | 1][i] != -1){
lazy[0][node << 1 | 1][i] ^= 1, lazy[1][node << 1 | 1][i] = 0;
}
lazy[1][node][i] = 0;
}
return ;
}
void build(int l, int r, int node){
for(int i = 0; i < 4; i++)
lazy[0][node][i] = -1, lazy[1][node][i] = 0;
if(l == r){
int x;
scanf("%d", &x);
for(int i = 0; i < 4; i++){
seg[node][i] = (x >> i) & 1;
}
return ;
}
int mid = (l + r) >> 1;
build(l, mid , node << 1);
build(mid + 1, r, node << 1 | 1);
for(int i = 0; i < 4; i++)
pushup(node, i);
return ;
}
int type, le, ri;
void update(int l, int r, int node, int i){
if(l >= le && r <= ri){
if(type == 1){
lazy[0][node][i] = 0;
lazy[1][node][i] = 0;
seg[node][i] = 0;
}
else if(type == 2){
lazy[0][node][i] = 1;
lazy[1][node][i] = 0;
seg[node][i] = r - l + 1;
}
else if(type == 3){
lazy[1][node][i] = lazy[1][node][i] ^ 1;
if(lazy[0][node][i] != -1)
lazy[0][node][i] ^= 1, lazy[1][node][i] = 0;
seg[node][i] = r - l + 1 - seg[node][i];
}
return ;
}
pushdown(node, l, r, i);
int mid = (l + r) >> 1;
if(mid >= le){
update(l, mid, node << 1, i);
}
if(mid < ri){
update(mid + 1, r, node << 1 | 1, i);
}
pushup(node, i);
return ;
}
int query(int l, int r, int node, int i){
if(l >= le && r <= ri){
return seg[node][i];
}
pushdown(node, l, r, i);
int ans = 0, mid = (l + r) >> 1;
if(mid >= le){
ans += query(l, mid, node << 1, i);
}
if(mid < ri){
ans += query(mid + 1, r, node << 1 | 1, i);
}
return ans;
}
int main(){
int T;
char ch[10];
cin >> T;
while(T--){
int n, m;
cin >> n >> m;
build(1, n, 1);
while(m--){
scanf("%s", ch);
if(ch[0] == 'S'){
scanf("%d %d", &le, &ri);
le++, ri++;
int ans = 0;
for(int i = 0; i < 4; i++){
ans += (1 << i) * query(1, n, 1, i);
}
printf("%d\n", ans);
}
else{
int opn;
scanf("%d %d %d", &opn, &le, &ri);
le++, ri++;
if(ch[0] == 'A'){
type = 1;
for(int i = 0; i < 4; i++){
if(((1 << i) & opn) == 0){
update(1, n, 1, i);
}
}
}
else if(ch[0] == 'O'){
type = 2;
for(int i = 0; i < 4; i++){
if((1 << i) & opn){
update(1, n, 1, i);
}
}
}
else{
type = 3;
for(int i = 0; i < 4; i++){
if((1 << i) & opn){
update(1, n, 1, i);
}
}
}
}
}
}
return 0;
}
感觉通过这个题理解线段树更深刻了。。。还是挺有意思的。