Loading

P7447 [Ynoi2007] rgxsxrs 题解

特别劝退的一道题目。

调了整整两天,码长 4.39kb -> 6.89kb

这道毒瘤题不仅卡时间,还卡空间。

题意

给定一个长为 \(n\) 的序列 \(a\),需要实现 \(m\) 次操作:

1 l r x:表示将区间 \([l,r]\) 中所有 \(>x\) 的元素减去 \(x\)

2 l r:表示询问区间 \([l,r]\) 的和,最小值,最大值。

思路

一看到题以为是类似第二分块的做法,后来发现了强制在线和巨大的值域范围。

首先看到要维护的值有和,最小值,最大值,可以想到可能可以用线段树维护。

但又由于这是 \(\text{Ynoi}\),所以还需要套一个分块。

考虑对于值域进行分块。

由于值域有 \(10^9\) 的大小,所以普通的根号分块显然不行,可以考虑 \(\log\) 分块。

具体来说,就是将值域分成 \(\log\) 个块,第 \(i\) 个块所存放的是 \(a^i-a^{i+1}\) 的值域。

在每个块内维护一颗动态开点线段树,以在原序列中的位置为下标,如果在减的过程中,值域不在这一块,则暴力删除修改。

如何进行修改。

  • 若此时节点最大值小于等于 \(x\),则可直接退出。

  • 若此时节点最小值大于 \(x\),同样可以直接打上标记然后退出。

  • 若都没有,则继续递归不断处理。

查询则可以直接像普通线段树一样查询。

考虑修改块的复杂度,每一个数最多改变 \(\log\) 次块,故复杂度为 \(O(n \log w \log n)\),其中 \(w\) 为值域。

在考虑查询和修改的复杂度,由于就是普通的在线段树上的操作,所以复杂度同样为 \(O(n \log w \log n)\)

这样的代码,你会获得 \(53pts\) 的好成绩。

你会发现你 \(\text{MLE}\) 掉了。

算一下空间,你就发现这道题居然也卡了空间。

考虑一个以时间换空间优化线段树空间的方法。

可以发现,线段树最下面几层的节点,占据了大量空间,考虑把底层换为分块来解决。

每次遍历到叶子节点的块时,直接暴力操作。

这样虽然时间效率变低了,但空间就被大量优化。

底层分块的实现可以尝试使用链表来维护,算一种常数小也比较好写的方法。

  1. 插入一个数时,直接接在此时的末尾的后面,复杂度 \(O(1)\)

  2. 删除一个数,同样是链表的基础操作,复杂度 \(O(1)\)

  3. 在做减法操作,下穿标记等操作时,只能遍历整个链表,复杂度 \(O(len)\),其中 \(len\) 为链表长度。

这个玩意总得复杂度可能不怎么好算,但感觉也不会太慢。

实测,底层块长开 \(100\)\(\log\) 分块开 \(14\) 进制,跑得比较快。

Code

posted @ 2022-08-01 11:24  JiaY19  阅读(67)  评论(0编辑  收藏  举报