莫队是永远也卡不住的。
—————— 许多dalaohtml
好久之前 老师讲了莫队,目的是让咱们理解分块思想,当时没时间作笔记(实际上是懒的),如今来补上OVO。chrome
区间求元素种类个数,每种种类元素个数……数组
一些看似线段树能作的题但实际上不知足加和性质的题。spa
大部分树状数组能作的题。指针
(若是有错误请大佬轻点打(\doge))htm
分块(我不会)blog
结构体排序(这个应该是驾轻就熟了)排序
疯狂卡常小技巧(不必定须要)utf-8
一个清明的脑子和一双别老手残的手(这个很重要)get
先分块,再排序,最后四个 while 结束一切。
有一个长度为 \(n\) 的序列,里面的元素为 \(c_i\) ,有 \(m\) 组询问,每次询问给出 \(l\) 和 \(r\),
求若是在 \([l,r]\) 里任意选两个数字选到相同数字的几率是多少,
输出答案以分数形式存在。
数据范围:\(n,m\in[1,50000]\),\(c_i,l,r\in[1,n]\) 。
很明显,这道题符合以前我说的莫队适用状况。
这时候,就是莫队发威的时候了!!(雾)
注意:如下过程可能会让人不明不白的,但我会在后面细讲的。
以每一个块为 \(\sqrt n\) 的大小分红 \(\sqrt n\) 个块 。
以每一个查询区间的左端点所在块顺序排序,
若是左端点所在块相同,就按照右端点所在块的序数奇偶性排序:
若右端点在奇数块,顺序排序;
若右端点在偶数块,倒叙排序。
开始遍历全部查询区间,设 \(l\) 和 \(r\) 两个指针做为遍历到的区间,
初始 \(l = 1\),\(r = 0\) 表明如今所在区间不存在,
而后从排好序的查询区间做为询问区间 \(ql\) 和 \(qr\),
若是 \(l < ql\),就让 \(l\) 向右移动直到与 \(ql\) 重合;
若是 \(l > ql\),就让 \(l\) 向左移动直到与 \(ql\) 重合;
若是 \(r < qr\),就让 \(r\) 向右移动直到与 \(qr\) 重合;
若是 \(r > qr\),就让 \(r\) 向左移动直到与 \(qr\) 重合,
同时让记录 \(l\) 到 \(r\) 之间全部种类元素的个数的数组 \(cnt\) 随遍历到的数字来加减计数。
最后求出几率的分母与分子,约分后所有输出。
先说第三步,便于下面的解释。
一开始,尚未遍历的时候,\(l\) 和 \(r\) 是初始化的,大概是这样:
请不要在乎奇怪的数字
很明显,此时 \(l\) 和 \(r\) 之间没有任何元素 。
咱们假定 \(ql = 2\),\(qr = 6\),
而此时 \(l\) 小于 \(ql\),因此 \(l\) 向右移动直到与 \(ql\) 重合 。
如今是这样的:
每次扫到一个数字(不管是 \(l\) 仍是 \(r\)),
若是以前扫到过,就说明 \(cnt\) 数组已经记录下来了这个元素,而如今再扫到说明要将这个元素从 \(l\) 到 \(r\) 这个区间中删去,这时候 \(cnt--\);
若是以前没有扫到过,就说明 \(cnt\) 数组没有记录下来,而如今扫到说明要将这个元素从 \(l\) 到 \(r\) 这个区间中加上,这时候 \(cnt++\) 。
如今继续模拟:
既然 \(l\) 和 \(ql\) 已经重合了,那么如今须要更新的是 \(r\) 的位置,因而 \(r\) 开始向 \(qr\) 靠拢直到重合:
因而如今 \(cnt\) 数组里存的是 \(ql\) 到 \(qr\) 的统计值,剩下的只须要在统计的时候按照求几率的公式来就好了,记得用 \(ans\) 数组记录下当前值,
毕竟查询下一个区间是在当前区间的基础下完成的。
分块没必要多说,若是不懂就看看 这个
分块的做用大概就是让上面说的两个指针 \(l\) 和 \(r\) 每次不会移动太大的距离:
\(r\) 从头至尾一直是从左到右的,而 \(l\) 则是在一个块里来回移动咣当。
莫队的奇偶性排序简直是最玄学的,经老师的一番解释,我大约明白了是怎么回事。
但我说不出来……
不过这不重要,最重要的是知道要这么作就好啦(护头,大佬轻点打/doge)
我的 jiao 得,这个才是最可贵(确信脸),若是不会请看大佬们的题解(这个不属于莫队里的)
若是看到这里还不懂的话,就看看这篇吧,
反正我是看这篇才会的(老师讲课永远 emmmm 懂得)
膜拜大佬 %%%