P4396 [AHOI2013]作业 题解

一道莫队好题。

我们需要考虑的是如何去统计一个区间内的符合题意的答案。

这里有两种做法:


做法一:树状数组/线段树。

我们可以开一个值域树状数组/值域线段树,在每一次莫队增/删的时候我们在对应位置进行单点修改操作,维护一下一个区间内的数的个数以及不同数的个数。

最后统计答案的时候,进行一次区间查找操作。

时间复杂度分析(不计预处理复杂度):

莫队转移复杂度是 \(O(n \sqrt{m})\),其中块长是 \(\dfrac{n}{\sqrt{m}}\),块数是 \(\sqrt{m}\),在转移过程中线段树单点修改和区间查询复杂度都是 \(O(\log n)\),因此总复杂度是 \(O(n \sqrt{m} \log n + \sqrt{m} \log n) \to O(n \sqrt{m} \log n)\)

由于 \(n,m\) 同阶,上述复杂度可以记为 \(O(n \sqrt{n} \log n)\)


做法二:

发现上述做法对于修改操作和查询操作复杂度是一样的,都是 \(O(\log n)\)

但是显然查询复杂度 \(O(\sqrt{m})\) 远小于转移复杂度 \(O(n \sqrt {m})\),因此我们可以考虑使用一种奇怪的数据结构使得其能够 \(O(1)\) 修改,\(O(?) \leq O(n)\) 修改。

这里就有一种数据结构叫做值域分块

对于修改操作,我们直接在对应位置上修改,复杂度 \(O(1)\)

查询的时候,我们进行一次区间查询,复杂度 \(O(\sqrt{V})\)\(V\) 是值域。

时间复杂度分析:

转移操作总复杂度是 \(O(n \sqrt{m})\),修改操作 \(O(1)\)

查询操作总复杂度是 \(O(\sqrt{m})\),单次查询 \(O(\sqrt{V})\)

因此总复杂度是 \(O(n\sqrt{m}+\sqrt{mV})\)

由于 \(n,m,V\) 同阶,上述复杂度可以简记为 \(O(n\sqrt{n}+\sqrt{n^2})=O(n\sqrt{n}+n)=O(n\sqrt{n})\),发现比做法一少了一个 \(\log\)


作者采用的是第二种做法。

Code:GitHub CodeBase-of-Plozia P4396 [AHOI2013]作业.cpp

posted @ 2022-04-17 18:02  Plozia  阅读(41)  评论(0编辑  收藏  举报