由於網上的關於差分的資料比較少,因此我根據我本身的理解編寫這篇博文。若是你有什麼問題,能夠聯繫我。算法
--------------------------------------------------------------------------------------------------------編程
有這樣一道題目:給你一個$m×n$的矩陣,而後使用$k$塊地毯鋪地。每片地毯都給出左下角和右上角座標。問全部地毯鋪完以後,還有多少個整點(所謂整點,即橫、縱座標均爲整數的點)沒有被地毯覆蓋。數組
固然,咱們很容易寫出以下的暴力程序(僞代碼):優化
solve(){ 暴力枚舉每張地毯 將全部被覆蓋的點均作上標記 最後再枚舉全部整點,若未被標記則ans+1 }
可是,很明顯,這個算法並不能拿到滿分,由於它的空間複雜度爲$\Theta(nm)$,可是時間複雜度卻可能達到$\Theta(mnk)$,對通常的比賽來講確定會沒法經過。調試
固然,有些大佬可能會說:開$m$棵線段樹能夠解決此問題。確定是能夠的,可是對於$NOIp$這種比賽來講,考試時間比較緊促,我是不太贊同這種算法的,由於這樣子編程複雜度過高,甚至可能出現沒法調試成功而影響了其它的題目或是影響本身的心情。blog
那麼,咱們應該如何優化這個算法呢?咱們考慮一下,主要的時間就是用在枚舉地毯和枚舉被地毯覆蓋的整點上,咱們能夠對這裏進行優化。由於對於每塊地毯,每一行,覆蓋的確定是一個連續區間。因此咱們能夠考慮一下前綴和。經過前綴和的方式考慮每一個點被地毯覆蓋的次數。以下面表格所示,假如地毯覆蓋了$[2,6]$一段($1$表示地毯在該行的左端點,其中表格第一行爲數組下標,第二行爲數組值):table
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
經過對數組求前綴和,咱們便能獲得如下的表格:class
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
咱們便會發現,經過這種前綴和的形式,可以在$\Theta(1)$的時間裏,實行對一行從某個左端點開始一段區間的修改。可是,咱們這個題目中地毯除了有左邊界,還有右邊界啊?沒關係,咱們在右邊界後面再減去$1$,就能夠保證沒有被覆蓋到的地方不會受到影響。而因爲右端點也包括在被覆蓋的範圍內,因此咱們要讓$r+1$減去$1$.用上面的表格,若是地毯在該行覆蓋了$[2,6]$一段,咱們就將原數組修改爲如下所示:循環
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0 | 1 | 0 | 0 | 0 | 0 | -1 | 0 |
求前綴和以後,數組就變成以下:遍歷
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
這樣子咱們就會發現,全部被地毯覆蓋的點都會變成$1$,而對於每一行,這種操做都是$\Theta(1)$的,因此k塊地毯所有考慮完畢的時間複雜度爲$\Theta(kn)$,最後每行作前綴和的時間複雜度爲$\Theta(nm)$,這樣子便對以上暴力算法進行了有效的優化。因此咱們能夠寫出如下的代碼(僞代碼):
solve(){ 從1號地毯考慮到第k塊地毯 對於每一塊地毯,從右上角座標行數循環到左下角行數 將每一行進行修改 對差分數組求前綴和並累計未被覆蓋地毯的點 }
近年的$NOIp$,彷佛對於樹上差分的題目考察愈來愈熱(參見$2015$年提升組 運輸計劃,$2016$年提升組 每天愛跑步)。這些題目都要知道在樹上從某個點到另外一個點的全部路徑。可是,暴力求解這種題目常常會$TLE$。這種題目須要使用樹上差分。在講樹上差分以前,首先須要知道樹的如下兩個性質:
(1)任意兩個節點之間有且只有一條路徑。
(2)根節點肯定時,一個節點只有一個父親節點
這兩個性質都很容易證實。那麼咱們知道,若是假設咱們要考慮的是從$u$到$v$的路徑,$u$與$v$的$lca$是$a$,那麼很明顯,若是路徑中有一點$u'$已經被訪問了,且$u'$≠$a$,那麼$u$'的父親也必定會被訪問,這是根據以上性質能夠推出的。因此,咱們能夠將路徑拆分紅兩條鏈,$u$->$a$和$a$->$v$。那麼樹上差分有兩種常見形式:(1)關於邊的差分;(2)關於節點的差分。
將邊拆成兩條鏈以後,咱們即可以像差分同樣來找到路徑了。用$cf[i]$表明從$i$到$i$的父親這一條路徑通過的次數。由於關於邊的差分,$a$是不在其中的,因此考慮鏈$u$->$a$,則就要使$cf[u]++$,$cf[a]--$。而後鏈$a$->$v$,也是$cf[v]++$,$cf[a]--$。因此合起來即是$cf[u]++$,$cf[v]++$,$cf[a]-=2$。而後,從根節點,對於每個節點$x$,都有以下的步驟:
(1)枚舉$x$的全部子節點$u$
(2)$dfs$全部子節點$u$
(3)$cf[x]+=cf[u]$
那麼,爲何可以保證這樣全部的邊都可以遍歷到呢?由於咱們剛剛已經說了,若是路徑中有一點$u'$已經被訪問了,且$u'$≠$a$,那麼$u'$的父親也必定會被訪問。因此$u'$被訪問幾回,它的父親也就由於$u'$被訪問了幾回。因此就可以找出全部被訪問的邊與訪問的次數了。路徑求交等一系列問題就是經過這個來解決的。由於每一個點都只會遍歷一次,因此其時間複雜度爲$\Theta(n)$.
仍是與和邊的差分同樣,對於所要求的路徑,拆分紅兩條鏈。步驟也和上面同樣,可是也有一些不一樣,由於關於點,$u$與$v$的$lca$是須要包括進去的,因此要把$lca$包括在某一條鏈中,用$cf[i]$表示$i$被訪問的次數。最後對$cf$數組的操做即是$cf[u]++$,$cf[v]++$,$cf[a]--$,$cf[father[a]]--$。其時間複雜度也是同樣的$\Theta(n)$.
--------------------------------------------------------------------------------------------------------
經過以上的描述,若是你仍是不太能理解,那麼如下兩個題目可能能夠幫助你理解:
USACO 最大流(樹上差分)https://www.luogu.org/problem/show?pid=3128
NOIp2015 運輸計劃(樹上差分+二分)https://www.luogu.org/problem/show?pid=2680