1.定義及理解:
dfs序是深度優先遍歷一顆樹的時候產生的時間戳序列,可以將樹形結構有序地轉化為線性結構,從而將樹上問題轉化為線性問題
這時,我們就可以用許多優秀的資料結構,比如維護區間的線段樹、樹狀陣列等方便地處理樹上問題啦!
2.重要的性質
(1)設in[x]表示第一次dfs到x節點的時間戳,out[x]表示從x節點退出的時候的時間戳,num[time]表示time的時間戳所對應的
樹上節點是哪乙個。其中,num陣列是我們新得到的dfs序列,是一段線性節點序。
顯然,一顆以x節點為根的子樹在dfs序中必然佔據一段連續的區間[in[x],out[x]],而這段區間內的所有節點是這顆子樹的所有子節點。
code:
void dfs(int u,int fa)
out[u]=++cnt;//記錄離開x的時間戳
dfs[cnt]=u;//更新dfs序
}(2)問題一:單點修改,子樹和查詢:
求出整棵樹的dfs序,直接單點修改節點x對應在dfs序上的值,然後子樹和也就是求區間和,考慮用樹狀陣列或線段樹維護即可!
問題二:樹鏈修改,單點查詢:
修改(u,v)節點間的值,比如全部加上a
那麼我們考慮對四條鏈進行操作!
考慮u--->root,v--->root,lca(u,v)--->root,fa(lca(uv))--->root!
我們將前兩條路徑上的所有點加a,後兩條路徑的所有點減去a
可以用樹狀陣列維護這個dfs序,序列上依次對應每個dfs序上對應節點的權值!
然後修改?這個樹狀陣列其實維護的是dfs序的權值字首和!
get_sum(x) 表示1-x的差分字首和
add(l[u],a),add(l[v],a),add(lca(u,v),-a),add(fa(lca(u,v)),-a);
val(x)=get_sum(r[x])-get_sum(l[x]-1);
問題三:子樹整體修改,查詢單點到根節點的權值和,以及單點的權值
比如我們對於以x節點為根的子樹整體加a
我們對於所有節點,維護兩個值
乙個是tag,另乙個是節點的自身權值;
我們提供兩種操作:
1)直接進行單點修改
2)將以x為根的子樹整體加a
操作1,直接單點修改即可 操作2,對於所有點,tag+=a,然後節點自身的權值,增加乙個-(dep[x]-1)*a;這個是為了抵消不在這個以x為根
的子樹內的節點的貢獻!詢問節點y到根的權值和的時候,我們返回:tag*dep[y]+val[y];
然後操作即可!
例題:poj3321
洛谷:p3178 樹上操作
dfs學習筆記整理
dfs題型 1.連通塊 就是標記畫圖,算出每個小模組個數 2.尋找到達終點的途徑數 理解dfs其實會無數次到達終點後,每次到達就ans 就可以算出,但是注意回溯時vis x y 要清0.例如下面這個 include include includeusing namespace std const i...
dfs學習筆記 1
這篇文大概就是自己在學習過程中學到的一些模板和注意事項吧,emmm在此先膜拜各路大佬orz 先貼核心 void dfs 引數用來表示狀態 if 越界或者是不合法狀態 return if 特殊狀態 剪枝 return for 擴充套件方式 要注意路徑記錄和節點重判!然後接下來是一些最最最基礎的dfs的...
dfs序 樹狀陣列
the first line contains an integer n n 100,000 which is the number of the forks in the tree.output for every inquiry,output the correspond answer per ...