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.

188 lines
8.3 KiB

2 years ago
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m, l1, r1, l2, r2;
// 线段树维护区间最大连续子序列和
#define ls u << 1
#define rs u << 1 | 1
struct Node {
int l, r;
int lmax, rmax; // 从左开始的连续脑洞个数,从右开始的连续脑洞个数
int sum; // 区间正常脑组织个数
int len; // 区间长度
int ans; // 区间最大的连续脑洞数量
int tag; // 懒标记
} tr[N << 2];
void pushup(int u) {
tr[u].sum = tr[ls].sum + tr[rs].sum; // 区间正常脑组织个数
if (tr[ls].lmax == tr[ls].len) // 左边都是0
tr[u].lmax = tr[ls].len + tr[rs].lmax; // 父亲左最长=左子整个长+右子树左最长
else
tr[u].lmax = tr[ls].lmax;
if (tr[rs].rmax == tr[rs].len) // 右边都是0
tr[u].rmax = tr[rs].len + tr[ls].rmax; // 父亲右最长=右子整个长+左子树右最长
else
tr[u].rmax = tr[rs].rmax;
// 三种情况,分情况讨论
tr[u].ans = max({tr[ls].ans, tr[rs].ans, tr[ls].rmax + tr[rs].lmax});
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r, tr[u].tag = -1; // 边界,懒标记初始化
tr[u].len = r - l + 1; // 区间长度,虽然记录了l,r,也可以现用现算,但一次计算还是代码更短,但内存会更大
if (l == r) {
tr[u].sum = 1; // 初始化时,是没有脑洞的,都是正常的脑组织
tr[u].ans = tr[u].lmax = tr[u].rmax = 0; // 因为默认值是1所以不写也行,但好习惯是写就写全,不用默认值
/* ans 区间最大的连续脑洞数量 = 0
lmax = 0
rmax = 0
*/
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
pushup(u);
}
/* 功能在u节点管控的区间完整命中的情况下更新u节点的统计信息并且变更u节点的懒标记为tag
Q:chang线西
使u:
modify
pushdownu
change(u,tag)
change(u,tag)线modify
pushdownchange(u,tag)change
*/
void change(int u, int tag) {
// 根据懒标记的不同结合业务逻辑对于u管辖的区间进行统计信息的变更(tr[u].len不需要变更它不是统计信息它是属性)
// ① 单个懒标记:不同的数值表示不同的含义,处理逻辑不同,比如本题 √
// ② 单个懒标记:不同的数值表示相同的含义,处理逻辑相同,比如都是+tag
// ③ 多个懒标记不同的tag代表不同的业务逻辑
if (tag == 0) {
tr[u].ans = tr[u].lmax = tr[u].rmax = tr[u].len;
tr[u].sum = 0;
}
if (tag == 1) {
tr[u].ans = tr[u].lmax = tr[u].rmax = 0;
tr[u].sum = tr[u].len;
}
// 记录当前区间的懒标记,以后可以向下进行传递
tr[u].tag = tag;
}
// 下传懒标记,让左右儿子进行统计信息修改和懒标记修改
void pushdown(int u) {
if (~tr[u].tag) {
// 递归处理左右子树
change(ls, tr[u].tag), change(rs, tr[u].tag);
// 下传完毕,懒标记清空
tr[u].tag = -1;
}
}
// 将区间[L,R]修改为v
void modify(int u, int L, int R, int v) {
int l = tr[u].l, r = tr[u].r;
if (l >= L && r <= R) { // 如果完整命中那么根据修改的值对于整体区间的统计信息进行计算同时对于整体区间打加修改的懒标记v
change(u, v);
return;
}
if (l > R || r < L) return; // 如果与当前区间无交集,那么返回
// 下传懒标记
pushdown(u);
// 分裂
modify(ls, L, R, v), modify(rs, L, R, v);
// 向上汇总信息
pushup(u);
}
// 查询区间内数字0的个数
int query0(int u, int L, int R) {
int l = tr[u].l, r = tr[u].r;
if (l >= L && r <= R) return tr[u].len - tr[u].sum;
if (l > R || r < L) return 0;
pushdown(u);
return query0(ls, L, R) + query0(rs, L, R);
}
// 查询区间内数字1的个数
int query1(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 query1(ls, L, R) + query1(rs, L, R);
}
// 查询区间内连续数字0的长度
int query2(int u, int L, int R) {
int l = tr[u].l, r = tr[u].r;
if (l >= L && r <= R) return tr[u].ans; // 区间最大的连续脑洞数量
if (l > R || r < L) return 0;
pushdown(u);
/*三种情况:
1 : query2(ls, L, R)
2 : query2(rs, L, R)
3+
*/
return max({query2(ls, L, R), query2(rs, L, R), min(tr[ls].rmax, tr[rs].l - L) + min(tr[rs].lmax, R - tr[ls].r)});
}
// 操作2
void solve() {
cin >> l2 >> r2;
int sum = query1(1, l1, r1); // 数字1的个数
if (sum == 0) return; // 特判如果没有数字1就没有继续的必要了你准备用一个区间里的1去填充别人家的空闲区域结果你一个都没有那还填什么填
// 将区[l1,r1]所有位置修改为0把所有数字1都取走
modify(1, l1, r1, 0);
/*
=-1,=00=11
build-1,00
tag=00 -1
l2,
[l2,r2]01 sum1x,
[l2,x]1
x10sumx,
使0query0,[l2,x]
0xmodify(1,l2,x,1)
*/
int l = l2, r = r2;
while (l < r) {
int mid = (l + r) >> 1;
if (query0(1, l2, mid) >= sum) // 在以root=1的树中[l2,mid]这个区间内查找数字0的个数,如果左边的0位置数量多于sum,则只用递归左侧即可
r = mid;
else
l = mid + 1;
}
modify(1, l2, l, 1);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("P4344.in", "r", stdin);
#endif
// 加快读入
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> m;
// 构建线段树
build(1, 1, n);
int op;
while (m--) {
cin >> op >> l1 >> r1;
if (op == 0) modify(1, l1, r1, 0); // SHTSC 挖了一个范围为 [l, r]的脑洞。
if (op == 1) solve(); // 把操作2封装成一个solve()函数,可以更好的理清代码的逻辑
if (op == 2) cout << (query2(1, l1, r1)) << endl; // 连续数字0的最大长度
}
return 0;
}