我貌似和全部的數據結構都有些誤會。。。。。。算法
在處理一些修改查詢問題的時候,咱們能夠利用分治的思想,好比說把一個線性的數據不斷分紅一棵二叉樹,也就是咱們所說的線段樹,這樣咱們就能夠在logn的時限裏作到修改和查詢。同理咱們也能夠把數據分紅一個只有兩層的樹(算上根節點三層),每一個節點分紅sqrt(該節點大小)個節點,這就是咱們所說的分塊了。數組
這裏咱們主要講解分塊的思想:數據結構
話很少說先上一張圖:3d
如圖,咱們這就是一個分好塊的結構。blog
那麼,這個結構有個什麼用呢?很明顯,咱們能夠用它維護單點修改與查詢,區間的修改,查詢。。。排序
看上去有些眼熟,是否是,想起來了些什麼?模板
沒錯,就是線段樹那幾個操做。那麼如今咱們繼續,能夠發現分塊的修改和查詢操做都是O(sqrt(n))的。那還要分塊有個什麼用??變量
咱們先來看這麼一道題:重構
咱們剛開始看的時候可能以爲能夠用數據結構實現,什麼線段樹主席樹平衡樹均可以想想,不過一會你就會發現,它們都不可行,過了一會,你會發現有一種線段樹套平衡樹的方法或許可作,只不過代碼複雜度很高,並且估計要調不短的時間吧。這個時候就是分塊出場的時候了。二叉樹
爲何這個題不能經過線段樹實現,緣由就在於第二個操做過於煩人,線段樹維護的區間信息沒法找出單點的,若是要查找一個區間中的全部點,沒法獲得一個讓人滿意的複雜度。那麼爲何分塊就能夠使人滿意了呢??
首先咱們說明幾個接下來常常會說的名詞:
整塊:查詢範圍內徹底包括的塊,也就是範圍內第二層的塊。
零散塊:查詢範圍內除了整塊其餘的部分,是第三層的塊,也就是一個一個的點。
接着聲明幾個變量:
那麼咱們再修改的時候,對於區間內全部的整塊,咱們O(1)的打上加法標記便可。
那麼對於全部的零散塊呢?
咱們發現,咱們最多有兩個部分的零散塊,其中每一個部分裏最多有不超過sqrt(n)個點,咱們直接暴力枚舉下標修改便可,那麼整個修改操做的複雜度就是O(sqrt(n));
那麼咱們再查詢的時候,對於每一個整塊,咱們能夠知道它是徹底有序的,因此咱們能夠直接二分查找返回。對於零散塊,咱們仍然選擇暴力枚舉,那麼查詢操做的複雜度就是O(sqrt(nlogn))。
因此整個操做咱們就能夠在O(qsqrt(nlogn))的複雜度內完成了。
筆者秉承懂了算法不看模板的法則,本身手動實現了一下這個題目。接下來分塊講述一下代碼的意義:
這一部分就是預處理而且分塊的部分,和塊的左右區間的處理。
而後在你的分塊主體開始以前,必需要有的一條語句:
這條語句就是先把b數組的每個塊中的數據排好序,這樣要否則頗有可能找的時候因爲每一塊的b數組尚未排序致使查找錯誤。
而後就是分塊的主體部分,咱們分紅修改和查詢分別看一下:
這個就是修改操做。要注意的是,在讀入的x和y點屬於同一個塊的時候咱們要特殊操做。
若是不在同一個塊裏,咱們就遍歷包含的全部整塊,打上標記。而後對於左右那兩個零散塊,咱們直接枚舉就能夠了。爲何用的是a數組而不是b數組?由於b數組在排完序以後下標表明的意義是數的大小順序,而不是咱們一開始讀入的順序,不能直接修改b數組。還有一個就是,這裏筆者爲了打上去方便,修改b數組的時候用了sort,這樣咱們的複雜度就增長了一個sqrt(logn)可是咱們使用歸併重構零散塊的話仍是能夠達到sqrt(n)的。對於 在同一個塊裏的,咱們也是直接暴力枚舉就能夠了。
爲何這些零散塊能夠暴力枚舉,複雜度不會很高麼?不會的,在修改每個零散塊的時候,咱們最多也就修改sqrt(n)個點,因此並不會有特別高的額複雜度。
這個是查詢操做,怎麼查找剛纔都已經說過了,一樣零散塊咱們就暴力枚舉便可。
那麼這道題就這麼完成了。
純粹讓你用分塊完成的題目比較少,可是咱們能夠用這東西水一水分,畢竟這玩意比線段樹什麼的好寫多了。