题目链接

题目

题目描述

lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作:

0 a b 把[a, b]区间内的所有数全变成0

1 a b 把[a, b]区间内的所有数全变成1

2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0

3 a b 询问[a, b]区间内总共有多少个1

4 a b 询问[a, b]区间内最多有多少个连续的1

对于每一种询问操作,lxhgww都需要给出回答,聪明的程序员们,你们能帮助他吗?

输入描述

输入数据第一行包括2个数,n和m,分别表示序列的长度和操作数目

第二行包括n个数,表示序列的初始状态

接下来m行,每行3个数,op, a, b,(0 ≤ op ≤ 4,0 ≤ a ≤ b)

输出描述

对于每一个询问操作,输出一行,包括1个数,表示其对应的答案

示例1

输入

10 10
0 0 0 1 1 0 1 0 1 1
1 0 2
3 0 5
2 2 2
4 0 4
0 3 6
2 3 7
4 2 8
1 0 5
0 5 6
3 3 9

输出

5
2
6
5

备注

对于30%的数据, \(1\le n,m \le 1000\) ;

对于100%的数据, \(1\le n,m \le 10^5\) 。

题解

知识点:线段树。

这一道题维护的信息较多需要逐一分析。

为了方便求区间长度,还有取反的操作,我们将 \(0,1\) 的信息都维护一下,但接下来只讲 \(1\) 的部分, \(0\) 同 \(1\) 就不讲了。

首先需要维护的是 \(1\) 的数量 \(sum1\) ,以及连续 \(1\) 个数的最大值 \(max1\) 。

在合并时, \(sum1\) 直接加即可。 \(max1\) 不仅要取子区间的 \(max1\) , 还需要考虑左子区间从右端点开始连续的 \(1\) ,以及右子区间从左端点开始连续的 \(1\) ,两部分拼起来的长度。因此还需要维护,区间从左端点开始连续 \(1\) 的个数 \(left1\) ,从右端点开始连续 \(1\) 的个数 \(right1\) 。

对于 \(left1,right1\) ,在合并时,要考虑一个特殊情况,左子区间 \(left1\) 等于区间长度(可用 \(sum0 + sum1\) 表示),那么他可以与右子区间的 \(left1\) 相加,得到区间的 \(left1\) , \(right1\) 同理。除此之外,直接继承即可。

因此,区间信息需要维护 \(sum0/1,max0/1,left0/1,right0/1\) 。

区间修改需要维护三种修改标记:全 \(0\) 、全 \(1\) 、取反。三种的区间修改都十分好实现,前两种直接改为区间长度,取反交换 \(0/1\) 即可。另外,考虑到标记下传需要一个标记表示没有修改,即单位元值,以供函数特判。

因此,区间修改需要维护 \(0/1/2/3\) (无修改、全 \(0\) 、全 \(1\) 、取反)。

懒标记的修改需要分类讨论:

  1. 修改为未修改,则新标记维持原状。
  2. 修改为全 \(0\) ,则新标记为全 \(0\) 。
  3. 修改为全 \(1\) ,则新标记为全 \(1\) 。
  4. 修改为取反,则分类讨论:
    1. 若原标记为未修改,则新标记为取反。
    2. 若原标记为全 \(0\) ,则新标记为全 \(1\) 。
    3. 若原标记为全 \(1\) ,则新标记为全 \(0\) 。
    4. 若原标记为取反,则新标记为未修改。

于是所有信息就维护完了。

时间复杂度 \(O((n+m) \log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; struct T {
int sum0, sum1;
int max0, max1;
int left0, left1;
int right0, right1;
static T e() {
return {
0,0,
0,0,
0,0,
0,0
};
}
friend T operator+(const T &a, const T &b) {
return{
a.sum0 + b.sum0,a.sum1 + b.sum1,
max({a.max0,b.max0,a.right0 + b.left0}),max({a.max1,b.max1,a.right1 + b.left1}),
a.left0 == a.sum0 + a.sum1 ? a.left0 + b.left0 : a.left0,a.left1 == a.sum0 + a.sum1 ? a.left1 + b.left1 : a.left1,
b.right0 == b.sum0 + b.sum1 ? b.right0 + a.right0 : b.right0,b.right1 == b.sum0 + b.sum1 ? b.right1 + a.right1 : b.right1
};
}
};
struct F {
int op;
static F e() { return{ 0 }; }
T operator()(const T &x) {
if (op == 0) return x;
else if (op == 1) return {
x.sum0 + x.sum1,0,
x.sum0 + x.sum1,0,
x.sum0 + x.sum1,0,
x.sum0 + x.sum1,0
};
else if (op == 2) return{
0,x.sum0 + x.sum1,
0,x.sum0 + x.sum1,
0,x.sum0 + x.sum1,
0,x.sum0 + x.sum1
};
else return{
x.sum1,x.sum0,
x.max1,x.max0,
x.left1,x.left0,
x.right1,x.right0
};
}
F operator() (const F &g) {
if (op == 0) return g;
else if (op == 1) return { 1 };
else if (op == 2) return { 2 };
else {
if (g.op == 0) return { 3 };
else if (g.op == 1) return { 2 };
else if (g.op == 2) return { 1 };
else return { 0 };
}
}
}; template<class T, class F>
class SegmentTreeLazy {
int n;
vector<T> node;
vector<F> lazy; void push_down(int rt) {
node[rt << 1] = lazy[rt](node[rt << 1]);
lazy[rt << 1] = lazy[rt](lazy[rt << 1]);
node[rt << 1 | 1] = lazy[rt](node[rt << 1 | 1]);
lazy[rt << 1 | 1] = lazy[rt](lazy[rt << 1 | 1]);
lazy[rt] = F::e();
} void update(int rt, int l, int r, int x, int y, F f) {
if (r < x || y < l) return;
if (x <= l && r <= y) return node[rt] = f(node[rt]), lazy[rt] = f(lazy[rt]), void();
push_down(rt);
int mid = l + r >> 1;
update(rt << 1, l, mid, x, y, f);
update(rt << 1 | 1, mid + 1, r, x, y, f);
node[rt] = node[rt << 1] + node[rt << 1 | 1];
} T query(int rt, int l, int r, int x, int y) {
if (r < x || y < l) return T::e();
if (x <= l && r <= y) return node[rt];
push_down(rt);
int mid = l + r >> 1;
return query(rt << 1, l, mid, x, y) + query(rt << 1 | 1, mid + 1, r, x, y);
} public:
SegmentTreeLazy(int _n = 0) { init(_n); }
SegmentTreeLazy(int _n, const vector<T> &src) { init(_n, src); }
void init(int _n) {
n = _n;
node.assign(n << 2, T::e());
lazy.assign(n << 2, F::e());
}
void init(int _n, const vector<T> &src) {
init(_n);
function<void(int, int, int)> build = [&](int rt, int l, int r) {
if (l == r) return node[rt] = src[l], void();
int mid = l + r >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
node[rt] = node[rt << 1] + node[rt << 1 | 1];
};
build(1, 1, n);
} void update(int x, int y, const F &f) { update(1, 1, n, x, y, f); } T query(int x, int y) { return query(1, 1, n, x, y); }
}; int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
vector<T> a(n + 1);
for (int i = 1;i <= n;i++) {
int x;
cin >> x;
a[i] = { 1 - x,x,1 - x,x,1 - x,x,1 - x,x };
}
SegmentTreeLazy<T, F> sgt(n, a);
while (m--) {
int op, l, r;
cin >> op >> l >> r;
l++, r++;
if (op == 0) sgt.update(l, r, { 1 });
else if (op == 1) sgt.update(l, r, { 2 });
else if (op == 2) sgt.update(l, r, { 3 });
else if (op == 3) cout << sgt.query(l, r).sum1 << '\n';
else cout << sgt.query(l, r).max1 << '\n';
}
return 0;
}

NC20279 [SCOI2010]序列操作的更多相关文章

  1. bzoj 1858: [Scoi2010]序列操作

    1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MB 线段树,对于每个区间需要分别维护左右和中间的1和0连续个数,并在op=4时特殊 ...

  2. BZOJ 1858: [Scoi2010]序列操作( 线段树 )

    略恶心的线段树...不过只要弄清楚了AC应该不难.... ---------------------------------------------------------------- #inclu ...

  3. bzoj1858[Scoi2010]序列操作 线段树

    1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 3079  Solved: 1475[Submit][Statu ...

  4. BZOJ_1858_[Scoi2010]序列操作_线段树

    BZOJ_1858_[Scoi2010]序列操作_线段树 Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询 ...

  5. 【题解】Luogu P2572 [SCOI2010]序列操作

    原题传送门:P2572 [SCOI2010]序列操作 这题好弱智啊 裸的珂朵莉树 前置芝士:珂朵莉树 窝博客里对珂朵莉树的介绍 没什么好说的自己看看吧 操作1:把区间内所有数推平成0,珂朵莉树基本操作 ...

  6. P2572 [SCOI2010]序列操作

    对自己 & \(RNG\) : 骄兵必败 \(lpl\)加油! P2572 [SCOI2010]序列操作 题目描述 lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要 ...

  7. BZOJ1858 [Scoi2010]序列操作(线段树)

    题目链接 [Scoi2010]序列操作 考验代码能力的一道好题. 思想还是很简单的(直接上线段树),但是比较难写. #include <bits/stdc++.h> using names ...

  8. (WAWAWAWAWAWA) BZOJ 1858: [Scoi2010]序列操作

    二次联通门 : BZOJ 1858: [Scoi2010]序列操作 /* BZOJ 1858: [Scoi2010]序列操作 已经... 没有什么好怕的的了... 16K的代码... 调个MMP啊.. ...

  9. 1858: [Scoi2010]序列操作

    1858: [Scoi2010]序列操作 Time Limit: 10 Sec Memory Limit: 64 MB Submit: 3397 Solved: 1624 [Submit][Statu ...

  10. SCOI2010 序列操作

    2421 序列操作 http://codevs.cn/problem/2421/ 2010年省队选拔赛四川   题目描述 Description lxhgww最近收到了一个01序列,序列里面包含了n个 ...

随机推荐

  1. 功能测试--Fiddler

    Fiddler(更推荐Charles,很好用) 1.fiddler是什么?------客户端的所有请求都要先经过fiddler,然后转发到服务器:反之,服务器的所有响应,也会先经过fiddler,然后 ...

  2. 使用Eclipse快速开发jsp和.编码问题、JSP页面元素以及request对象

    在IDEA中创建的Web项目: 浏览器可以直接访问WebContent中的文件. 例如http:// localhost:8888/MyJspProject/index1.jsp其中的index1.j ...

  3. [UnityAI]行为树的中断机制

    参考链接: https://www.cnblogs.com/01zxs/p/9863715.html https://blog.csdn.net/AcmHonor/article/details/12 ...

  4. SpringBoot——实现WebService接口服务端以及客户端开发

    参考:https://blog.csdn.net/qq_43842093/article/details/123076587 https://www.cnblogs.com/yinyl/p/14197 ...

  5. 02-Spring基于XML的Bean属性注入

    属性值注入:就是给属性赋值 创建一个Account类: public class Account implements Serializable { private int aid; private ...

  6. SQL作业编辑报错 无法将COM组件......

    在命令行运行下列命令 数据库为2005cd C:\Program Files\Microsoft SQL Server\90\DTS\Binnregsvr32 dts.dll

  7. 联邦学习开源框架FATE架构

    作者:京东科技 葛星宇 1.前言 本文除特殊说明外,所指的都是fate 1.9版本. fate资料存在着多处版本功能与发布的文档不匹配的情况,各个模块都有独立的文档,功能又有关联,坑比较多,首先要理清 ...

  8. 音视频编解码流程与如何使用 FFMPEG 命令进行音视频处理

    一.前言 FFMPEG 是特别强大的专门用于处理音视频的开源库.你既可以使用它的 API 对音视频进行处理,也可以使用它提供的工具,如 ffmpeg, ffplay, ffprobe,来编辑你的音视频 ...

  9. CF859E题解

    题意简述 翻译很清楚了 题目解法 如果一个人想去的位置上原来坐着人,那么他要坐到这个位置上,就要把原来的人赶走. 原来的人被赶走了,就只能去想去的位置.如果那个位置上有人,又要把那个人赶走. 我们发现 ...

  10. 【Eolink】Apikit V10.8.0 版本发布!增加支持 DUBBO、TCP、SOAP 、HSF、UDP 的接口协议

    Apikit 最新功能来袭! 我们在这个版本实现了接口管理和测试能力的全面升级,包括且不限于: 新增功能速览: 增加支持 DUBBO.TCP.SOAP .HSF.UDP 接口文档和协议 接口文档可自动 ...