1. 程式人生 > >dfs序七個經典問題(轉)

dfs序七個經典問題(轉)

就是 一個 圖片 https 前綴 info 經典 角度 等價

我這個人不怎麽喜歡寫輕重鏈剖分和LCT 還是喜歡dfs序、括號序列之類的

畢竟線段樹好寫多了

然後就有了這篇轉載的文章 寫在這邊以後有時間看看

原文鏈接:https://www.cnblogs.com/weeping/p/6847112.html

參考自:《數據結構漫談》-許昊然

dfs序是樹在dfs先序遍歷時的序列,將樹形結構轉化成序列問題處理。

dfs有一個很好的性質:一棵子樹所在的位置處於一個連續區間中。

ps:deep[x]為x的深度,l[x]為dfs序中x的位置,r[x]為dfs序中x子樹的結束位置

1.點修改,子樹和查詢

  在dfs序中,子樹處於一個連續區間中。所以這題可以轉化為:點修改,區間查詢。用樹狀數組或線段樹即可。

2.樹鏈修改,單點查詢

  將一條樹鏈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])

  同問題三,用兩個樹狀數組或線段樹即可。

dfs序七個經典問題(轉)