[Ynoi2014]人人本着正义之名(题解)

我们不打代码,我们只是思想的搬运工。

题意

[Ynoi2014]人人本着正义之名

背景
略
题目描述
你需要帮珂朵莉维护一个长为n的01序列a,有m个操作:
1 l r : 把区间[l,r]的数变成0
2 l r : 把区间[l,r]的数变成1
3 l r : [l,r-1]内所有数a[i],变为a[i]与a[i+1]按位或的值,这些数同时进行这个操作
4 l r : [l+1,r]内所有数a[i],变为a[i]与a[i-1]按位或的值,这些数同时进行这个操作
5 l r : [l,r-1]内所有数a[i],变为a[i]与a[i+1]按位与的值,这些数同时进行这个操作
6 l r : [l+1,r]内所有数a[i],变为a[i]与a[i-1]按位与的值,这些数同时进行这个操作
7 l r : 查询区间[l,r]的和

输入格式
第一行两个数表示n和m
第二行n个数表示序列a
之后m行,每行三个数opt l r,表示是哪一种操作以及操作对应的区间

输出格式
对于每个查询操作输出一行一个数表示答案

输入输出样例
输入 #1 复制
5 5
0 1 0 0 1
3 2 5
5 2 5
2 2 2
6 1 5
7 1 5
输出 #1 复制
1
说明/提示
Idea:nzhtl1477,Solution:nzhtl1477,Code:nzhtl1477,Data:nzhtl1477

序列每次的样子:
0 1 0 0 1
0 1 0 1 1
0 0 0 1 1
0 1 0 1 1
0 0 0 0 1
0 0 0 0 1

对于30%的数据,n,m<=1000

对于50%的数据,n,m<=100000

对于另外20%的数据,操作和序列均随机生成

对于100%的数据,n,m<=1000000

题意

这里不给出代码,只给出思想。

思想来想大部分来自同机房的HYY巨佬。我一直陷入了差分的狂潮,HYY巨还是巨呀。

我们把每个点按照位置为权值插入到平衡树里面,但是我们发现了一件事情,我们可以把一串1或0当成区间节点插入进去。

然后我们继续推规律:

\(00111000\)

我们对整个区间操作4一下,变成了:\(00011000\)

我们发现长度为\(1\)的串\(l++\),长度为\(0\)的串\(r++\)

也就是说对于操作4,区间内的所有整块的\(1\) \(l++\)\(0\)的块 \(r++\),而对于边界,举举例子就知道如果坐落在块中,那么是完全没有影响的,也就是边界独立处理,而且为了答案的正确,我们需要把边界改一下,缩到一个区间节点上,因为我们不可能从中间开始维护。

在这里插入图片描述

其他的操作也可以类似这样子,当然边界特判自己手推吧。。。

我们中间的打打标记就行了,对于记录信息0或1的,分别有不同的两个标记,\(l\)加多少,\(r\)加多少,然后对于我们每个子树,都有三个信息,就是子树内有多少个\(0\)区间、多少个\(1\)区间和子树和,这些都是可以平衡树解决的。

但是区间推平怎么做呢?

首先,如果边界坐落在了整块中间(不是边界),如:11100 中的[2,4]变成1,那么我们就看看坐落的块是不是同个颜色的,是就直接包含这个整块,如果不是,就把这个区间分成两个点,然后把这个操作的范围拿出来,而且对于每个操作最多新建两个点,所以\(log\)暴力维护。

在这里插入图片描述
当然边界问题还有个特例,就是如果坐落在了边界的话,且边界坐落的整块与修改值相反,那么还要把隔壁的块添加进来。(下一步你就知道为什么了。)

在这里插入图片描述
然后我们就遍历这个树,把这个树中的节点合并成一个大的区间节点(这就可以解释为什么在边界的话,我们要跳到隔壁了,因为我们要保证相邻的两个区间维护的值一定是不同的。)。

但是这个操作不是\(O(n)\)的吗?

你想啊,每次操作最多加两个区间,也就是从头到尾区间的个数加上多出来的,都是\(O(n)\)级别的,我们每合并一次就没了一个,那么这个操作总时间就是最多\(O(n)\)了,均摊\(O(1)\),但是因为还有平衡树的操作,所以是\(O(logn)\)的。

两个操作都是如此。

完结撒花。

你在想桃子。

有没有注意到一点,就是当进行\(3,4,5,6\)操作时,一些长度为\(1\)的区间减一下就每了!

那么就有区间合并了?

那么我们要怎么去找这些区间呢。

我们首先要知道如何找到一个区间左右区间的所在的位置,那么对于每个区间,我们都可以维护一个\(head,tail\)指针,表示左右区间的下标,然后对于每个合并、添点、分裂、删点,我们都可以极快的维护,对时间复杂度不造成影响。

那么我们对于每个节点,再维护一个子树内区间长度最小值,然后看看最小值是不是为\(0\),为\(0\)就删除,并且把他左右区间合并,并且再继续看看新的最小值是不是\(0\)

而对于左右区间合并,我们要把左右区间节点分离出原来的子树,然后合并,再添回去,而且,删除的区间和这两个区间维护的值相反,所以这两个区间的长度不会减少的,不用担心这两个区间也是被删除的对象,不过需要注意的是这两个区间节点不一定在\([l,r]\)范围内(虽然影响应该不是很大)。。。

但是你这样子我可以卡到\(nlogn\)呀。

我们继续证明均摊。

因为每次合并少\(1\)个区间,而我们单次合并的复杂度是\(O(logn)\),且是\(O(n)\)级别的区间个数,那么总复杂度最多\(O(nlogn)\),均摊\(log\)

那么我们的时间复杂度总的就是\(O(nlogn)\)

注意:此篇题解建议用fhq treap实现,以及这里n,m同阶。

这里没有代码,这道题目码量太大,有错请在评论区指出,谢谢O(∩_∩)O。骗吃骗喝骗评论

猜你喜欢

转载自www.cnblogs.com/zhangjianjunab/p/11728549.html