一、内容
题意:给定一组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;
}