You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

158 lines
4.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
// 线段树,求区间连续序列长度
#define mid ((l + r) >> 1)
#define ls (u << 1)
#define rs (u << 1 | 1)
struct Node {
int l, r, len;
int sum; // 区间中数字1个数
int mx[2], lx[2], rx[2]; // 区间内连续最长数字1左起最长数字1长度右起最长数字1长度
int turn, assign; // 取反懒标记,赋值懒标记
} tr[N << 2];
void pushup(int u) {
// 更新需要更新的属性本题是1+2+2+2=7个需要更新的属性
tr[u].sum = tr[ls].sum + tr[rs].sum; // 区间中1的个数为左右子树中1的个数和
for (int i = 0; i <= 1; i++) {
tr[u].lx[i] = tr[ls].lx[i]; // 继承左儿子的左端点最多连续0/1个数
if (tr[ls].sum == i * tr[ls].len) tr[u].lx[i] += tr[rs].lx[i]; // 如果左儿子全是0/1那么加上右儿子的左端点最长连续0/1个数
tr[u].rx[i] = tr[rs].rx[i];
if (tr[rs].sum == i * tr[rs].len) tr[u].rx[i] += tr[ls].rx[i];
tr[u].mx[i] = max({tr[ls].mx[i], tr[rs].mx[i], tr[ls].rx[i] + tr[rs].lx[i]});
}
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r, tr[u].len = r - l + 1;
tr[u].assign = -1;
if (l == r) {
int v;
cin >> v;
tr[u].sum = v;
tr[u].mx[v] = tr[u].lx[v] = tr[u].rx[v] = 1;
return;
}
build(ls, l, mid), build(rs, mid + 1, r);
pushup(u);
}
void change_turn(int u) {
// 处理统计信息
tr[u].sum = tr[u].len - tr[u].sum;
swap(tr[u].mx[1], tr[u].mx[0]);
swap(tr[u].lx[1], tr[u].lx[0]);
swap(tr[u].rx[1], tr[u].rx[0]);
// 处理懒标记
if (tr[u].assign != -1)
tr[u].assign ^= 1;
else
tr[u].turn ^= 1;
}
void change_all(int u, int v) {
// 处理统计信息
tr[u].mx[1] = tr[u].lx[1] = tr[u].rx[1] = tr[u].sum = v * tr[u].len;
tr[u].mx[0] = tr[u].lx[0] = tr[u].rx[0] = tr[u].len - tr[u].sum;
// 处理懒标记
tr[u].assign = v;
tr[u].turn = 0;
}
/*
懒标记传递流程
(1) modify
① 如果整体命中调用change()函数处理当前区间
② 如果未整体命中pushdown(),然后分裂
(2) change()函数:根据懒标记
① 修改当前区间统计信息
② 整理当前区间懒标记,以便向下推送
(3) pushdown
如果存在某个懒标记调用change()处理左右儿子区间,清空懒标记
*/
void pushdown(int u) { // 下传懒标记
// 多个懒标记的处理原则:谁的优先级高就先处理谁,很明显,本题中的赋值运算优先级高
if (tr[u].assign != -1) { // 如果存在赋值懒标记
change_all(ls, tr[u].assign); // 向左儿子传递懒标记
change_all(rs, tr[u].assign); // 向右儿子传递懒标记
tr[u].assign = -1; // 清空赋值懒标记
}
if (tr[u].turn) { // 如果存在取反的懒标记
change_turn(ls); // 向左儿子传递懒标记
change_turn(rs); // 向右儿子传递懒标记
tr[u].turn = 0; // 清空取反懒标记
}
}
void modify_turn(int u, int L, int R) {
int l = tr[u].l, r = tr[u].r;
if (l >= L && r <= R) {
change_turn(u);
return;
}
if (l > R || r < L) return;
pushdown(u);
modify_turn(ls, L, R), modify_turn(rs, L, R);
pushup(u);
}
void modify_assign(int u, int L, int R, int v) {
int l = tr[u].l, r = tr[u].r;
if (l >= L && r <= R) {
change_all(u, v);
return;
}
if (l > R || r < L) return;
pushdown(u);
modify_assign(ls, L, R, v), modify_assign(rs, L, R, v);
pushup(u);
}
int query_all(int u, int L, int R) {
int l = tr[u].l, r = tr[u].r;
if (l >= L && r <= R) return tr[u].sum;
if (l > R || r < L) return 0;
pushdown(u);
return query_all(ls, L, R) + query_all(rs, L, R);
}
int query_continue(int u, int L, int R) {
int l = tr[u].l, r = tr[u].r;
if (L <= l && r <= R) return tr[u].mx[1];
if (r < L || l > R) return 0;
pushdown(u);
return max({query_continue(ls, L, R), query_continue(rs, L, R), min(mid - L + 1, tr[ls].rx[1]) + min(R - mid, tr[rs].lx[1])});
}
int main() {
#ifndef ONLINE_JUDGE
freopen("P2572.in", "r", stdin);
#endif
// 加快读入
ios::sync_with_stdio(false), cin.tie(0);
int n, m;
cin >> n >> m;
build(1, 0, n - 1);
while (m--) {
int op, l, r;
cin >> op >> l >> r;
if (op == 0) modify_assign(1, l, r, 0);
if (op == 1) modify_assign(1, l, r, 1);
if (op == 2) modify_turn(1, l, r);
if (op == 3) printf("%d\n", query_all(1, l, r)); // 区间内总共有多少个1
if (op == 4) printf("%d\n", query_continue(1, l, r)); // 区间内最多有多少个连续的1
}
return 0;
}