FZU 2105 Digits Count 线段树

本文深入讲解了一种数据结构——线段树,通过一个具体的题目来阐述如何使用线段树进行区间更新和查询操作。该文详细解释了如何处理区间AND、OR、XOR等操作,并介绍了如何维护懒标记以提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:点击打开链接

题意: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;
}
感觉通过这个题理解线段树更深刻了。。。还是挺有意思的。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值