update-2018.07.23: 原文問題五思路描述有誤,已更正。數組
參考自:《數據結構漫談》-許昊然數據結構
dfs序是樹在dfs先序遍歷時的序列,將樹形結構轉化成序列問題處理。3d
dfs有一個很好的性質:一棵子樹所在的位置處於一個連續區間中。blog
ps:deep[x]爲x的深度,l[x]爲dfs序中x的位置,r[x]爲dfs序中x子樹的結束位置get
1.點修改,子樹和查詢date
在dfs序中,子樹處於一個連續區間中。因此這題能夠轉化爲:點修改,區間查詢。用樹狀數組或線段樹便可。遍歷
2.樹鏈修改,單點查詢im
將一條樹鏈x,y上的全部點的權值加v。這個問題能夠等價爲:數據
1).x到根節點的鏈上全部節點權值加v。查詢
2).y到根節點的鏈上全部節點權值加v。
3).lca(x,y)到根節點的鏈上全部節點權值和減v。
4).fa(lca(x,y))到根節點的鏈上全部節點權值和減v。
上面四個操做能夠歸結爲:節點x到根節點鏈上全部節點的權值加減v。修改節點x權值,當且僅當y是x的祖先節點時,x對y的值有貢獻。
因此節點y的權值能夠轉化爲節點y的子樹節點貢獻和。從貢獻和的角度想:這就是點修改,區間和查詢問題。
修改樹鏈x,y等價於add(l[x],v),add(l[y],v),add(l[lca(x,y)],-v),add(l[fa(lca(x,y))],-v)。
查詢:get_sum(r[x])-get_sum(l[x]-1)
用樹狀數組或線段樹便可。
3.樹鏈修改,子樹和查詢
樹鏈修改部分同上一問題。下面考慮子樹和查詢問題:前一問是從貢獻的角度想,子樹和同理。
對於節點y其到根節點的權值和,考慮其子節點x的貢獻:w[x]*(deep[x]-deep[y]+1) = w[x]*(deep[x]+1)-w[x]*deep[y]
因此節點y的子樹和爲:
ps:公式中的v[i]爲手誤,應爲w[i]。
因此用兩個樹狀數組或線段樹便可:
第一個維護∑w[i]*(deep[i]+1):支持操做單點修改,區間和查詢。(這也就是問題2)
第二個維護∑ w[i]:支持操做單點修改,區間查詢。(這其實也是問題2)
4.單點更新,樹鏈和查詢
樹鏈和查詢與樹鏈修改相似,樹鏈和(x,y)等於下面四個部分和相加:
1).x到根節點的鏈上全部節點權值加。
2).y到根節點的鏈上全部節點權值加。
3).lca(x,y)到根節點的鏈上全部節點權值和的-1倍。
4).fa(lca(x,y))到根節點的鏈上全部節點權值和的-1倍。
因此問題轉化爲:查詢點x到根節點的鏈上的全部節點權值和。
修改節點x權值,當且僅當y是x的子孫節點時,x對y的值有貢獻。
差分前綴和,y的權值等於dfs中[1,l[y]]的區間和。
單點修改:add(l[x],v),add(r[x]+1,-v);
5.子樹修改,單點查詢
修改節點x的子樹權值,在dfs序上就是區間修改,單點權值查詢就是單點查詢。
區間修改,單點查詢問題:樹狀數組或線段樹便可;
6.子樹修改,子樹和查詢
題目等價與區間修改,區間查詢問題。用樹狀數組或線段樹便可。
7.子樹修改,樹鏈查詢
樹鏈查詢同上,等價爲根節點到y節點的鏈上全部節點和問題。
修改節點x的子樹權值,當且僅當y是x的子孫節點時(或y等於x),x對y的值有貢獻。
x對根節點到y節點的鏈上全部節點和的貢獻爲:w[x]*(deep[y]-deep[x]+1)=w[x]*deep[y]-w[x]*(1-deep[x])
同問題三,用兩個樹狀數組或線段樹便可。