题目链接

题目

题目描述

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. 【已解决】appium启动会话时遇到的的几种问题

    第一种: 1.启动会话时一直卡在加载界面,报错log信息及截图如下 [ADB] Creating ADB subprocess with args: ["-P",5037,&quo ...

  2. 痞子衡嵌入式:我当选了2019年度官方论坛i.MXRT板块的顶级贡献者

    痞子衡桌上有一个联邦快递公司件,放了近一个月了,是同事代领的,痞子衡一直没有拆开.今天心血来潮,决定拆开瞧一瞧,这一拆就拆出了一天的好心情. 原来快递里面是一件i.MXRT产品纪念T-shirt,还有 ...

  3. 基于 RocketMQ Connect 构建数据流转处理平台

    本文作者:周波,阿里云智能高级开发工程师, Apache RocketMQ Committer . 01 从问题中来的RocketMQ Connect 在电商系统.金融系统及物流系统,我们经常可以看到 ...

  4. 给生活加点惊喜,做创意生活的原型设计师丨编程挑战赛 x 选手分享

    前言 做产品的大都跳过一个坑:我有了一个很好的产品创意,只差一个程序员帮我实现编程了. 事实上从产品创意到落地上线,中间需要经过非常复杂的过程,细节的逻辑流程才是难点,创意不能落地,并不值钱. 本文作 ...

  5. day09-拦截器&文件上传

    拦截器&文件上传 1.拦截器-Interceptor 1.1拦截器概念 拦截器 拦截器(Interceptor):是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行. ...

  6. Oracle 函数整理

    一.字符控制函数 函数 结果 CONCAT('Hello','World') HelloWorld SUBSTR('HelloWorld',1,5) Hello LENGTH('HelloWorld' ...

  7. 人工智能机器学习底层原理剖析,人造神经元,您一定能看懂,通俗解释把AI“黑话”转化为“白话文”

    按照固有思维方式,人们总以为人工智能是一个莫测高深的行业,这个行业的人都是高智商人群,无论是写文章还是和人讲话,总是讳莫如深,接着就是蹦出一些"高级"词汇,什么"神经网络 ...

  8. 一文总结你需要的OpenCV操作

    目录 一.OpenCV简介 1.1 OpenCV是什么 1.2 安装及使用 二.图像的基础 2.1 成像原理 2.2 图像格式 2.3 颜色空间 三.OpenCV基础操作 3.1 图像的读取.显示.保 ...

  9. Spring(Ioc和Bean的作用域)

    Spring Spring为简化开发而生,让程序员只关心核心业务的实现,尽可能的不在关注非业务逻辑代码(事务控制,安全日志等). 1,Spring八大模块 这八大模块组成了Spring 1.1 Spr ...

  10. Linux配置为代理服务器,代理微信

    1.安装squid(全程使用root账号,如使用其他请注意权限问题) yum install squid 2.生成代理的账号密码 #如果没有htpasswd命令 先安装sudo yum install ...