洛谷 P5066 [Ynoi2014] 人人本着正义之名

洛谷 P5066 [Ynoi2014] 人人本着正义之名

Problem

对一个01序列进行以下\(m\)个操作:

  1. 区间覆盖为0
  2. 区间覆盖为1
  3. 将区间\([l,r-1]\)中的数\(a_i\)同时变为\(a_i\)\(a_{i+1}\)按位或的值
  4. 将区间\([l+1,r]\)中的数\(a_i\)同时变为\(a_i\)\(a_{i-1}\)按位或的值
  5. 将区间\([l,r-1]\)中的数\(a_i\)同时变为\(a_i\)\(a_{i+1}\)按位与的值
  6. 将区间\([l+1,r]\)中的数\(a_i\)同时变为\(a_i\)\(a_{i-1}\)按位与的值
  7. 查询区间和

强制在线。

\(1\le n,m\le 3\times10^6,0\le a_i\le1\).

Solution

刚开始思考的时候,看到数据范围中有着随机生成这个部分分,想到了珂朵莉树(毕竟背景是珂朵莉嘛~)。

但是貌似珂朵莉无法对操作\(3,4,5,6\)进行很好的维护。

又注意到这个序列只有01,这里可是一个突破口!

我们手玩一下样例中的01001

首先分段:

\[[0][1][00][1] \]

操作\(3\)

\[[11][0][11] \]

可以发现,所有的0段的最右边的0都变成了1,也就是所有的1段的长度向左扩展了1位(如果它的左边还有的话)。

操作\(4,5,6\)也类似,每次一种段只会有边界上的改变。

这个貌似就很好维护了。

但是我们都知道,珂朵莉维护的是一段值相同的区间,却不一定随时都是极长的。

即,我们需要让一个0段的左右保证是1段(或者没有)。

我们手写一个平衡树(而不用set)来维护这些子段。因为左/右端点移动量显然是可以叠加的,可以在平衡树上打标记维护。

但是有两个问题:如何保证每个子段都是极长的?如何保证没有空的子段?

如果子段不是极长的,那么就会有相邻的同值子段,其中一个移动边界时会占领另一个,但是显然不需要移动。对于这个问题比较简单,只要在区间赋值的时候多向两遍拓展即可。其他的操作都是不会造成这种非极长子段的。

当我们在移动边界的时候,会出现一个子段的长度已经变成了0的情况,会导致总段数剧增,无法保证时间复杂度。

在平衡树上再维护两个值:子树中最短的0/1段长度即可。

发现有长度为0的段时直接暴力删除。

平衡树的一个节点维护了:左端点,右端点,子段的值,子树和,子树中0段的数量,子树中1段的数量,子树内最短的0段,子树内最短的1段,lazy标记。

然后这道题就结束了。

Code

按照传统(?),只给部分代码供参考。有问题也可以私信(如果我还没AFO的话)

剪切板

Other

唉,2021年了,这道题需要一点卡常了……

原本\(Ynoi\)是很友好的,但是有些人用错误的时间复杂度乱搞过去了,所以只能加强加强再加强,导致正解想要过去也比较困难了……

\(\textbf{2021-03-31 08:00:25 thus,AC.}\)

附上debug日志。祝您早日AC。

posted @ 2021-03-31 08:40  Vanilla_chan  阅读(213)  评论(0编辑  收藏  举报