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\)
之後便只需要: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]萌萌噠 對區間的每個點一一對應連並查集