NC20279 [SCOI2010]序列操作
题目
题目描述
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\) 、取反)。
懒标记的修改需要分类讨论:
- 修改为未修改,则新标记维持原状。
- 修改为全 \(0\) ,则新标记为全 \(0\) 。
- 修改为全 \(1\) ,则新标记为全 \(1\) 。
- 修改为取反,则分类讨论:
- 若原标记为未修改,则新标记为取反。
- 若原标记为全 \(0\) ,则新标记为全 \(1\) 。
- 若原标记为全 \(1\) ,则新标记为全 \(0\) 。
- 若原标记为取反,则新标记为未修改。
 
于是所有信息就维护完了。
时间复杂度 \(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]序列操作的更多相关文章
- bzoj 1858: [Scoi2010]序列操作
		1858: [Scoi2010]序列操作 Time Limit: 10 Sec Memory Limit: 64 MB 线段树,对于每个区间需要分别维护左右和中间的1和0连续个数,并在op=4时特殊 ... 
- BZOJ 1858: [Scoi2010]序列操作( 线段树 )
		略恶心的线段树...不过只要弄清楚了AC应该不难.... ---------------------------------------------------------------- #inclu ... 
- bzoj1858[Scoi2010]序列操作 线段树
		1858: [Scoi2010]序列操作 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 3079 Solved: 1475[Submit][Statu ... 
- BZOJ_1858_[Scoi2010]序列操作_线段树
		BZOJ_1858_[Scoi2010]序列操作_线段树 Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询 ... 
- 【题解】Luogu P2572 [SCOI2010]序列操作
		原题传送门:P2572 [SCOI2010]序列操作 这题好弱智啊 裸的珂朵莉树 前置芝士:珂朵莉树 窝博客里对珂朵莉树的介绍 没什么好说的自己看看吧 操作1:把区间内所有数推平成0,珂朵莉树基本操作 ... 
- P2572 [SCOI2010]序列操作
		对自己 & \(RNG\) : 骄兵必败 \(lpl\)加油! P2572 [SCOI2010]序列操作 题目描述 lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要 ... 
- BZOJ1858 [Scoi2010]序列操作(线段树)
		题目链接 [Scoi2010]序列操作 考验代码能力的一道好题. 思想还是很简单的(直接上线段树),但是比较难写. #include <bits/stdc++.h> using names ... 
- (WAWAWAWAWAWA) BZOJ 1858: [Scoi2010]序列操作
		二次联通门 : BZOJ 1858: [Scoi2010]序列操作 /* BZOJ 1858: [Scoi2010]序列操作 已经... 没有什么好怕的的了... 16K的代码... 调个MMP啊.. ... 
- 1858: [Scoi2010]序列操作
		1858: [Scoi2010]序列操作 Time Limit: 10 Sec Memory Limit: 64 MB Submit: 3397 Solved: 1624 [Submit][Statu ... 
- SCOI2010 序列操作
		2421 序列操作 http://codevs.cn/problem/2421/ 2010年省队选拔赛四川 题目描述 Description lxhgww最近收到了一个01序列,序列里面包含了n个 ... 
随机推荐
- 5-CSRF漏洞
			1.CSRF介绍 Csrf漏洞也被称为One Click Attack或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用.尽管听起来像xss漏洞,但是它与xss漏 ... 
- vue--v-model 的三种修饰符lazy、number、trim
			Vue--v-model的三种修饰符lazy.number.trim v-model.lazy: 值修改操作完成之后才会发生变化. v-model.number: 只修改时,保持其值为Number类 ... 
- 使用 HTML、CSS 和 JavaScript 制作的随机密码生成器
			----上图 ------MVC 创建的视图 ,视图名称为A@{ Layout = null;} <!DOCTYPE html><style type="text/css& ... 
- MQ异常断开
			ActiveMQ:No operations allowed after statement closed问题及解决办法 ActiveMQ版本:5.5.1 现象: 系统现象:部分消息发送失败,失败 ... 
- 服务器链接工具MobaXterm
			链接:https://pan.baidu.com/s/15zC4JC0XOKYI1lN5bkB3fw 提取码:9zc8 每次使用都需要输入密码.修改密码: 链接:https://pan.baidu.c ... 
- 摘记:找到程式中 不會被執行到的 code
			定义 https://en.wikipedia.org/wiki/Unreachable_codeRemove unreachable code refactoringhttps://docs.mic ... 
- Neo4j学习(1)--安装
			1.访问官网,下载Community版本,注意在下载页面已经提示了登录时的默认用户名和密码neo4j/neo4j.我使用的版本为3.4.10,jdk要求为java8 2.安装Windows版本,最好参 ... 
- CSS函数var
			/*全局变量保存的地方*/ :root { --main-bg-color: red; /* 变量名必须以--开头 */ } var(custom-property-name, value) 值 描述 ... 
- 字符集编码cp936、ANSI、UNICODE、UTF-8、GB2312、GBK、GB18030、DBCS、UCS
			一直对字符的各种编码方式懵懵懂懂,什么ANSI.UNICODE.UTF-8.GB2312.GBK.DBCS.UCS--是不是看的很晕,假如您细细的阅读本文你一定可以清晰的理解他们.Let's go! ... 
- celery 使用
			celery 1.celery介绍 celery能用来做什么: 1.异步任务 2.定时任务 3.延迟任务 1.1 理解celery的运行原理 1.可以不依赖任何服务器 通过自身命令 启动服务 2.ce ... 
