莫隊是永遠也卡不住的。
—————— 許多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 懂得)
膜拜大佬 %%%