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

这篇博客主要解析了[Ynoi2014]人人本着正义之名的题目,讨论了如何使用平衡树处理区间操作问题。文章强调不关注代码,重点在于思路。通过分析操作4的规律,提出了将1或0串作为区间节点插入平衡树的方法,并探讨了边界处理和区间推平的策略。博客还提到了区间合并的维护方法,以及如何通过极快地更新head和tail指针找到区间位置。最终,作者证明了算法的时间复杂度为O(nlogn)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

题意

[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 00111000 00111000

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

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

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

在这里插入图片描述

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

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

但是区间推平怎么做呢?

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

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

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

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

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

两个操作都是如此。

完结撒花。

你在想桃子。

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

那么就有区间合并了?

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

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

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

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

但是你这样子我可以卡到 n l o g n nlogn nlogn呀。

我们继续证明均摊。

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

那么我们的时间复杂度总的就是 O ( n l o g n ) O(nlogn) O(nlogn)

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值