樹上差分,顧名思義,就是在樹上進行差分,以起到優化複雜度的目的。主要作用是對樹上的路徑進行修改和查詢操作,在修改多、查詢少的情況下複雜度比較優秀。實際上,樹上差分能夠實現的操作,用線段樹、樹剖、$lct$等等也可以實現,但它的優勢在於實現簡單,可以避免在考場上出現寫題五分鐘、除錯兩小時的情況 當然大佬可以忽略這點
在講解樹上差分之前,先講一下差分的思想。
差分,可以當做字首和的逆運算。既然是逆運算,運算方法自然就是相反的了。定義差分陣列$diff$,則運算法則為:(設原數列為$a$)
$$diff_i=a_i-a_$$
作個比較:
原數列947
59字首和9
1320
2534
差分陣列9-5
3-24字首和的差分陣列94
759差分陣列的字首和94
759顯然,原數列的字首和的差分陣列還是原數列,原數列的差分陣列的字首和也是原數列,這就是差分被稱為字首和的逆運算的原因,也是差分可以用來優化操作的原因。實際上,差分優化和字首和優化原理類似,只是實現相反。字首和優化常常對字首和陣列作差,差分優化也常常對差分陣列求字首和。
這樣,差分的概念就很清晰了。接下來開始進入正題。
樹上差分有什麼作用?舉個例子,如果題目要求對樹上的一段路徑進行操作,並詢問某個點或某條邊被經過的次數,樹上差分就可以派上用場了。這就是樹上差分的基本操作。
樹上差分,就是利用差分的性質,對路徑上的重要節點進行修改(而不是暴力全改),作為其差分陣列的值,最後在求值時,利用$dfs$遍歷求出差分陣列的字首和,就可以達到降低複雜度的目的。
樹上差分時需要求$lca$,不會的可以點選食用。對點
和邊的樹上差分原理相同,實現略有不同,這裡分開來講。
點差分設將兩點$u,v$之間路徑上的所有點權增加$x$,$o=lca(u,v)$,$o$的父親節點為$p$,則操作如下:
diff[u]+=x,diff[v]+=x,diff[o]-=x,diff[p]-=x;怎麼樣,是不是很簡單!原理也很簡單,舉個例子:
設原樹如下,現要將$2,3$之間路徑上的所有點的權值增加$3$,設原權值均為$0$。
則操作後有:
這樣,只要$dfs$一遍,遍歷時統計以每個節點為根的樹的節點的權值和,就是當前節點的最終權值! 是不是很厲害
就是差分的思想,這裡就不多說了。
邊差分思想一樣,講一下操作。
設將兩點$u,v$之間路徑上的所有邊權增加$x$,$o=lca(u,v)$,以每條邊兩端深度較大的節點儲存該邊的差分陣列,則操作如下:
diff[u]+=x,diff[v]+=x,diff[o]-=2*x;再舉個例子,還是上面那個圖 絕對不是我懶
則操作後有:
同樣地,只要$dfs$一遍,遍歷時統計以每個節點為根的樹的節點的權值和,就是當前節點到父親節點的邊的最終權值了!
是不是很厲害
至於為什麼點差分和邊差分的操作不一樣,很簡單,請讀者自己思考。
樹上差分主要還是學習思想吧!
習題:差分
綜合應用題:p1083
樹上差分
簡單模板題:p3128
綜合應用題:p2680
樹上差分的整理(點的樹上差分和邊的樹上差分)
點的樹上差分 若經過 u 到 v 的所有點,tmp u tmp v tmp lca u,v tmp parent lca u,v 0 例題 include using namespace std struct ss ss data 600010 int n,q int a 300010 head 6...
差分 樹上差分略解
差分 樹上差分略解 哈哈差分?先來看一道題 題目描述 給定包含 n個數的陣列 a1,a2,an.有 k次操作 每次操作把區間 l,r 加上v 最後求出數列每個位置的數。輸入格式 第一行,n.第二行,a1,a2,an下一行,k,下k行,每行乙個操作,格式為l,r,v 輸出格式 一行,更新後的陣列 樣例...
差分總結二 樹上差分
找這個樹上 重複經過的最多點 經過幾次 看這名字 就醉了orz 這題是 樹上差分 模板題 點差分 點差分的話 由於 lca 本身是有貢獻的 那麼d lca 用d lca父親 只要消掉影響 include using namespace std const int maxn 1e5 10 int n,...