1. 程式人生 > 其它 >(動態)點分治學習筆記

(動態)點分治學習筆記

\[\huge \rm 點分治 \]
\[\Large \rm 點分治思想 \]

\(\quad\)點分治用於處理大規模樹上路徑資訊問題。

\(\quad\)其基本思想是在子樹內處理完只跟子樹有關的資訊,再將其彙總到父節點處。遞迴時,不是簡單地訪問當前節點的兒子,而是求出子樹的重心,再向這個重心遞迴。


\[\Large \rm [LG3806]點分治1 \]

\(\quad\)點分治模板題。

\(\quad\)考慮在結點 \(u\) 處求解所有以 \(u\) 為拐點的鏈中是否有長度為 \(k\) 的鏈。遍歷時用一個桶記錄下所有以 \(u\) 為一個端點的鏈的長度即可。求解完再直接向子樹的重心遍歷,現在的問題又變成了一個相同的子問題。需要注意的是,所有的結點都會被作為重心統計一次,所以不存在漏掉的情況,利用點分治的遍歷方法會讓複雜度更優僅僅是因為每次子樹節點數減半,而非重複利用了什麼資訊。

\(\quad\)時間複雜度 \(\Theta(qn\log n)\).


\[\Large \rm [IOI2011]Race \]

\(\quad\)比前一題多記錄一個狀態,即每條長度為 \(k\) 的鏈的結點個數,這個很好計算,但需要注意的是,記錄結點個數的桶初值需賦為 \(\tt INF\),每次修改時取較小值。


\[\Large \rm [國家集訓隊]聰聰可可 \]

\(\quad\)記一個桶,分別記錄三種長度的路徑有多少條,將兒子一個個往裡加即可。


\[\Large \rm [LG4178]Tree \]

\(\quad\)點分治 \(+\) 樹狀陣列。

\(\quad\)考慮如何統計在點 \(u\)

處的答案。用樹狀陣列記錄在當前節點長度為 \(i\) 的鏈有多少條,然後直接對每條新加進的路徑查詢字首和即可。

\(\quad\)時間複雜度 \(\Theta(n\log^2n)\).


\[\Large \rm 點分樹\&動態點分治 \]

\(\quad\)觀察到點分治時作為根遍歷的結點也形成一棵樹,且它是包含原樹中所有節點,樹高為 \(\log n\) 的重構樹,在這棵樹上,由於樹高為 \(\log n\),所以原本一些很不對勁的暴力在這棵樹上都能有正確的複雜度。點分樹常用於解決與樹原形態無關的帶修改問題。

\(\quad\)有如下問題,設計一種資料結構,支援以下操作:

  • 線上查詢一個節點 \(u\)

    ,答案為 \(\sum_{v\in T}dist(u,v)\times w_v\).

  • 修改一個節點的點權。

\(\quad\)考慮建立點分樹 \(T'\),此時 \(T\) 中有用的資訊僅有兩點間點權,這個可以用樹剖 \(\rm LCA\) 較快地計算。

\(\quad\)\(T'\) 中,\(T'_x\) 表示以 \(x\) 號節點為根的子樹,令 \(f_u=\sum_{v\in T'_u}dist(u,v)\times a_v~,~sum_u=\sum_{v\in T'_u}a_v\).

\(\quad\)統計答案時從當前節點向上跳,假設當前跳到結點 \(fa_u\),答案更新為 \(ans+f_{fa_u}-f_u-sum_u\times dist(u,fa_u)+(sum_{fa_u}-sum_u)\times dist(fa_u,u)=f_{fa_u}-f_u+(sum_{fa_u}-2sum_u)\times dist(fa_u,u)\). 其意義是,計算所有結點 \(v\in T'_{fa_u}\cap~\overline{T'_u}\) 的貢獻。

\(\quad\)考慮對當前演算法進行優化,由於遍歷到根節點的順序中,有關 \(f\) 的計算為 \(f_u+(f_{fa_u}-f_u)+(f_{fa_{fa_u}}-f_{fa_u})+\cdots +(f_{root}-f_{pro\_root})=f_{root}\),故不用計算 \(f\),只需預處理出 \(f_{root}\) 即可。另外,考慮到在計算中關於兩點間距離只需求 \(dist(u,fa_u)\),所以可預處理出所有 \(dist(u,fa_u)\).

\(\quad\)對於動態點權修改操作,只牽涉到 \(f_{root}\)\(sum_s(v\in T'_s)\) 的修改,這些都可以在 \(\log n\) 的時間內完成。

\(\quad\)由於在點分樹中,樹高為 \(\log n\),所以暴力向上跳的次數僅有 \(\log n\) 次,故總時間複雜度為 \(\Theta[(n+q)\log n]\).


\[\Large \rm [LG6329]震波 \]

\(\quad\)點分樹 \(+\) 權值線段樹。

\(\quad\)\(f_u\) 為一顆記錄 \(T'_u\) 中資訊的動態開點權值線段樹,在其下標為 \(k\) 處的值為 \(\sum\limits_{v\in T'_u}^{dist(u,v)\leqslant k}w_v\).

\(\quad\)對於修改點權操作,直接在 \(T'\) 中從 \(u\) 往上跳,在當前結點的權值線段樹中修改點權即可,單次操作 \(\Theta(\log^2n)\)

\(\quad\)對於查詢操作,記 \(f_{u,0\to k}\) 表示權值線段樹 \(f_u\) 中,結點距離點 \(u\)\(0\)\(k\) 的點權之和,\(g_{u,0\to k}\) 表示權值線段樹 \(g_u\) 中,結點距離點 \(fa_u\)\(0\)\(k\) 的點權之和,這個可以 \(\Theta(\log n)\) 求。查詢一個節點 \(u\) 的答案時,從 \(u\) 開始往上跳,\(ans\) 初值為 \(f_{u,0\to k}\),每次更新答案 \(ans=ans+f_{now,0\to k-dist(u,now)}-g_{pro,0\to k-dist(u,now)}\),單次操作 \(\Theta(\log^2n)\).

\(\quad\)一個簡化程式碼的 \(\rm Trick\) : 可以一開始將權值線段樹的值都設為 \(0\),然後把初始權值看做 \(n\) 個修改權值的操作。

\(\quad\)總時間複雜度 \(\Theta[(n+q)\log^2n]\).