一、内容

题意:给定一组01字符串,给定5个操作,操作0将区间变为0,操作1区间变为1,操作2区间0变1,1变0,操作3查询区间1的总个数,操作4查询区间最大连续的1的个数。

二、思路

  • 用val来对01进行标记,初始值为-1,等于0代表这区间都是0,等于1代表区间都是1。
  • 用mx_1 来求出连续的1的最大个数。
  • flag记录是否转换,一个区间执行2次转化等于没执行, 所以每次对flag ^= 1。

三、代码

#include <cstdio>
#include <algorithm>
#define len(l, r) ((r) - (l) + (1))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) > (b) ? (b) : (a))
using namespace std;
const int maxn = 1e5 + 5;

//val 判断该区间上是0 还是1 初始为-1 
int val[maxn << 2], flag[maxn << 2];
// lx代表从左边开始连续的个数  mx代表区间中最大的连续数 
int lx_0[maxn << 2], rx_0[maxn << 2], mx_0[maxn << 2], lx_1[maxn << 2], rx_1[maxn << 2], mx_1[maxn << 2]; 
//记录0或者1的个数 
int sum_0[maxn << 2], sum_1[maxn << 2]; 
int t, n, m, code, x, y, ans, a[maxn];

//执行更新的0 1 2 三个操作 
void change(int id, int l, int r, int a, int rel) {
	if (a == 0) {
		//执行第一个操作   将一段区间修改为0
		lx_0[id] = rx_0[id] = mx_0[id] = sum_0[id] = len(l, r) ;   
		lx_1[id] = rx_1[id] = mx_1[id] = sum_1[id] = 0 ; 
		val[id] = 0;
		flag[id] = 0;
	} else if (a == 1) {
		//执行第二个操作
		lx_1[id] = rx_1[id] = mx_1[id] = sum_1[id] = len(l, r); 
		lx_0[id] = rx_0[id] = mx_0[id] = sum_0[id] = 0; 
		val[id] = 1;
		flag[id] = 0;
	}
	if (rel) {
		//代表执行第三个操作 0变1 1变0
		swap(lx_0[id], lx_1[id]);		 
		swap(rx_0[id], rx_1[id]);		 
		swap(mx_0[id], mx_1[id]);	 
		swap(sum_0[id], sum_1[id]);
		flag[id] ^= 1;
	}
} 	


void pushup(int id, int l, int r) {
	int ll = id << 1;	
	int rr = id << 1 | 1;
	int mid = (l + r) >> 1;
	lx_1[id] = lx_1[ll] + (lx_1[ll] == len(l, mid) ? lx_1[rr] : 0);
	lx_0[id] = lx_0[ll] + (lx_0[ll] == len(l, mid) ? lx_0[rr] : 0);
	rx_1[id] = rx_1[rr] + (rx_1[rr] == len(mid + 1, r) ? rx_1[ll] : 0);
	rx_0[id] = rx_0[rr] + (rx_0[rr] == len(mid + 1, r) ? rx_0[ll] : 0);
	mx_1[id] = max(mx_1[ll], max(mx_1[rr], lx_1[rr] + rx_1[ll])); 
	mx_0[id] = max(mx_0[ll], max(mx_0[rr], lx_0[rr] + rx_0[ll])); 
	sum_1[id] = sum_1[ll] + sum_1[rr];
	sum_0[id] = sum_0[ll] + sum_0[rr];
}

void build(int id, int l, int r) {
	val[id] = -1;
	lx_1[id] = lx_0[id] = rx_0[id] = rx_1[id] = mx_1[id] = mx_0[id] = sum_1[id] = sum_0[id] = flag[id] = 0;
	if (l == r) {
	 	change(id, l, r, a[l], 0);
		return; 
	} 
	int mid = (l + r) >> 1;
	build(id << 1, l, mid);
	build(id << 1 | 1, mid + 1, r);
	pushup(id, l, r);
} 
void pushdown(int id, int l, int r) {
	if (val[id] != -1 || flag[id]) {
		int mid = (l + r) >> 1;
		change(id << 1, l, mid, val[id], flag[id]);
		change(id << 1 | 1, mid + 1, r, val[id], flag[id]);
		flag[id] = 0;
		val[id] = -1;
	}
}
void update(int id, int l, int r, int x, int y, int a) {
	
	if (x <= l && r <= y) {
		if (a == 0 || a == 1) {
			//执行操作1 或者 2 
			change(id, l, r, a, 0);
		} else {
			//执行操作3 
			change(id, l, r, -1, 1);
		}
		return;
	}	
	int mid = (l + r) >> 1; 
	//进行pushdown
 	pushdown(id, l, r); 
	if (x <= mid) update(id << 1, l, mid, x, y, a); 
	if (y > mid) update(id << 1 | 1, mid + 1, r, x, y, a); 
	pushup(id, l, r);
}
int query(int id, int l, int r, int x, int y, int a) {
	if (x <= l && r <= y) {
		if (a == 3) return sum_1[id];
		return mx_1[id]; 
	}
	int mid = (l + r) >> 1;
	//由于区间合并问题 所以要分成3个部分 
	pushdown(id, l, r);
	if (y <= mid) {
		 //只在左边
		 return query(id << 1, l, mid, x, y, a); 
	} else if (x > mid){
		//只在右区间
		return query(id << 1 | 1, mid + 1, r, x, y, a); 
	} else {
	 	int left = query(id << 1, l, mid, x, y, a);
		int right = query(id << 1 | 1, mid + 1, r, x, y, a);
		//a== 3只是求1的总个数 
		if (a == 3) return left + right;
		//在这里由于lx_1【id<<1】 包含的区间大于查询的  所以要在查询的里面取一个最小值 
		//列如lx_1[id << 1] 代表 【1-5】,值为5   查询的区间是【2-5】只会查询4个1  
		int max3 = min(mid - x + 1, rx_1[id << 1]) + min(lx_1[id << 1 | 1], y - mid);
		return max(left, max(right, max3));
	}
}
 

int main() {
	scanf("%d", &t);
	while (t--) {
		scanf("%d%d", &n, &m);
		for (int i = 0; i < n; i++) {
			scanf("%d", a + i);
		} 
		build(1, 0, n - 1 );
		for (int i = 1; i <= m; i++) { 
			scanf("%d%d%d", &code, &x, &y);
			if (code == 0 || code == 1 || code == 2) {
				update(1, 0, n - 1, x, y, code);
			} else {
				printf("%d\n", query(1, 0, n - 1, x, y, code));
			}
		}
	}
	return 0;
}