1. 程式人生 > >Noip前的大抱佛腳----根號對數演算法

Noip前的大抱佛腳----根號對數演算法

根號演算法

分塊

數列分塊入門九題(hzwer)

  • 入門題1,2,3,4,5,7

問題:給一段區間打上標記後單點查詢

解法:主要是每塊維護一些標記,計算答案等,此類分塊較為簡單

注意:塊大小一般為\(\sqrt n\)

複雜度:\(O(n\sqrt n)\)

  • 入門題6

問題:每次朝數列中間插入一個元素,查詢第k個元素是什麼

解法:塊大小超過一定值後暴力重構!採用連結串列實現

複雜度:\(O(n\sqrt n)\)

  • 入門題8

問題:每次詢問一個區間內為\(c​\)的元素個數,並把整個區間改為\(c​\)

解法:維護一個區間覆蓋標記,如果塊內沒有標記就暴力修改

注意:複雜度分析要用到FlashHu的勢能分析

勢能(potential energy)是儲存於一個系統內的能量,也可以釋放或者轉化為其他形式的能量。

把一個塊攪亂,相當於給其增加\(O(\sqrt n)\)的勢能,表示其再次被打上全域性tag所需要的代價

每次操作最多把兩個塊攪亂,所以每次操作最多增加\(O(2\sqrt n)\)的勢能,同時整理好一個塊需要的代價是其勢能,並能把其勢能降為\(O(1)\)

這樣子最多攪亂\(n\)個塊,增加\(O(2n\sqrt n)\)的勢能,最多把他們所有的勢能都變成\(1\),複雜度為\(O(4n\sqrt n)=O(n\sqrt n)\)

理解:增加勢能需要相應代價,減少勢能也需要相應代價,對應分析其最大勢能即可得出複雜度

拓展:分析每次可以從棧中彈出多個元素的複雜度(還是\(O(n)\),其總勢能最大為\(O(n)\)

  • 入門題9

問題:線上維護區間最小眾數

解法:離線就可以用莫隊搞了

考慮分塊,分塊是一個很好的演算法,維護每個數在數列中的字首和是\(O(n^2)\)的時空複雜度,但是給分個塊就可以做到\(O(n\sqrt n)\)

一段區間的眾數一定屬於:A.零散塊內的數 B.整塊內的眾數,一共數量不超過\(\sqrt n\)

所以維護兩個陣列:\(f[i][j]\)表示從第\(i\)塊到第\(j\)

塊的眾數,這個可以\(O(\sqrt n\sqrt n\sqrt n)\)的預處理出來;\(g[i][j]\)表示離散化後的數字\(i\)在前\(j\)塊中出現的次數,這個可以\(O(n)\)賦值後\(O(n\sqrt n)\)統計字首和而得到

之後便只需要:A.統計每個數在整塊內的出現個數\(O(1×\sqrt n)\) B.統計零散塊中的數\(O(\sqrt n)\)

綜上,複雜度為\(O(n\sqrt n)\),完美通過此題/蒲公英

分塊出過什麼題

維護序列哈,支援一些線上的區間詢問

  • 區間tag單點查詢、區間查詢等等老掉牙的套路(但是考場上遇到維護序列的題這也是一種思想方式

  • 詢問位置\(\%p=k\)的數的權值和,要求支援單點修改,\(val\le 1W,n\le 15W\)(雜湊衝突)

對於\(p\le\sqrt n\),維護\(s[p][k]\)表示答案,這個可以\(O(n\sqrt n)\)掃一遍得到答案

對於\(p>\sqrt n\),可以開\(s[i][1k]\)表示第i塊的桶,統計字首和,然後暴力對\(\sqrt n\)的桶掃一遍

綜上,複雜度為\(O(n\sqrt n)\)

  • 求區間逆序對數,不修改,\(n\le 5W\)(Gty的妹子序列)

先預處理好\(s[i][j]\)表示從第\(i\)塊的開頭到\(j\)位置這一段區間產生的逆序對數,\(O(n\sqrt n logn)\)

然後查詢時剩下的左半截用主席樹暴力查詢,\(O(n\sqrt nlogn)\)

  • 單點修改,求最短字首使得字首\(gcd\)與字首\(xor\)的乘積恰好為\(x\)\(n\le 10W\)(公約數數列)

有一個奇妙的性質:一個數集的\(gcd\)在加入一個數後要麼不變,要麼至少\(/2\)

對每一塊維護一個gcd和、異或字首和、Map(維護異或字首和為x的位置)

然後修改就暴力重構該塊\(O(n\sqrt nlogn)\)

查詢就依次掃每一個塊,如果這個塊沒有使得gcd減小,那麼這個塊內每一個位置的字首gcd都相同,在Map中查詢是否有符合條件的異或和即可;如果gcd減小了就暴力一個一個找,由於上面的性質暴力掃的塊不會超過\(logn\)個。

總複雜度為\(O(n\sqrt nlogn)\)

  • 無修改詢問區間內數值\(\%p=k\)的元素個數,\(n\le 15W\)(考試9.20T3)

\(p\le\sqrt n\),對每塊維護\(s[i][p][k]\)表示答案,然後邊角暴力統計,\(O(n\sqrt n)\)

\(p>\sqrt n\) ,對每塊維護\(s[i][j]\)表示桶,然後做一個字首和暴力查詢,\(O(n\sqrt n)\)

簡直傻逼我做不出真是太菜了

莫隊

樹莫隊

有兩種方法,一種是把樹劃分成若干塊,然後暴力去移動其中一個端點;另一種是把樹的尤拉序摳出來(像括號序列,每個點出現兩次),轉化為序列問題後再用序列莫隊的方法。我學習的是拓展性更強的後者。兩者都要用\(vis[i]\)表示i這個點選了沒選,每次\(update\)就把\(vis[x]^=1\)

序列莫隊

無修

對序列位置分塊,每塊大小為\(\sqrt n\),將詢問離線,按照左端點所在塊為第一關鍵字右端點為第二關鍵字排序,每次暴力移動轉移,複雜度\(O(n\sqrt n)\)

帶修

塊大小為\(n^{\frac{2}{3}}\),按照左端點所在塊為第一關鍵字,右端點所在塊為第二關鍵字,操作時間為第三關鍵字排序(所有排序都是從小到大),複雜度\(O(n^{\frac{5}{3}})\)

分治

序列分治

一般適用於:一組詢問,物件是所有子區間的問題

把區間分治成為跨過中點的區間和左右分治

點分治

  • 樹上路徑摳成序列後DP(有結合律),可以採用點分治實現(牛客Wannafly24C網址

    實現方式:把詢問掛鏈,分治的時候,把分治區域的\(rt\)都設定為分治中心,同時給不同子樹標個不同的\(Tag\)(同一子樹相同)。每到一個結點便掃一遍詢問,當詢問的另一個點也在同樣的分治區域,並且\(Tag\)不同,便計算貢獻。

    這樣能夠保證不重不漏地計算所有的貢獻,複雜度為\(O(nlogn*DP)\),為點分治×DP的複雜度(詢問只會被掃log次)

倍增

並查集倍增

[SCOI2016]萌萌噠 對區間的每個點一一對應連並查集