深夜来放毒,哈哈,闲着没事看了一下收藏里面的文章,莫队算法应该是放了很久了,但是都不想看(其实是看不懂),zz。
为了清空一下收藏夹,于是印着头皮看了一下莫队算法,一篇好的讲解是多么的重要的。看了好几篇,首先文章的排版我就不想看了
主要思想在于:对于离线查询,按照朴素的思想复杂度为O(n^2),但是莫队算法的厉害之处在于只是改变查询的顺序而已。一开始不太看得懂,看了两三篇基本能够明白了。
重点:
(1)是分块思想,将结点分块,然后排序,第一顺序是左区间L,按照所处的块来区分,第二顺序是右区间R,按照大小来排序。
(2)设计add,remove函数,不同的问法设计不同,主要的修改地方是这两个函数。
(3)就是左右区间的移动了,4个循环目的是使得currentL和currentR到达查询的位置L和R,算法的高效就在于移动的次数,通过调整顺序,使得移动的次数在O(n*sqrt(n))。
add(position):
count[array[position]]++
if count[array[position]] == 3:
answer++
remove(position):
count[array[position]]--
if count[array[position]] == 2:
answer--
currentL = 0
currentR = 0
answer = 0
count[] = 0
for each query:
// currentL should go to L, currentR should go to R
while currentL < L:
remove(currentL)
currentL++
while currentL > L:
add(currentL)
currentL--
while currentR < R:
add(currentR)
currentR++
while currentR > R:
remove(currentR)
currentR--
output answer
由于好久好久没有写过C++了,现在也没时间刷题。所以只是了解一下思想而已,至于练手还是以后吧。
下面的一些相关的题:
习题和示例代码
DQUERY – SPOJ:区间内不同元素的数量=出现次数>=1的元素个数,所以跟以上讨论的问题是一样的。
点击此处查看示例代码
注意:该代码提交会超时,加上更快的输入输出(传说中的输入输出挂?) 就能AC。为了使代码整洁,去掉了快的输入输出。(评论中说将remove 和add 声明为inline内联函数就可以AC。)
Powerful array – CF Div1 D: 这道题是必须用莫队算法的例子。我找不到任何其它解法。 CF Div1 D 意味着这是一道难题。看看用了莫队算法就多简单了。 你只需要修改上述代码中的add(), remove() 函数。
GERALD07 – Codechef
GERALD3 – Codechef
Tree and Queries – CF Div1 D
Powerful Array – CF Div1 D
Jeff and Removing Periods – CF Div1 D
Sherlock and Inversions – Codechef