\(noip\)考了,趕緊補一發。
不得不說網上的題解還是不錯的ljq的代碼吼啊
- 一開始看的博客
- 模板
其實我感覺看博客不如看別人優秀的代碼來的快- 朴素\(dp\)的想法就是\(f_{i,01}\)表示當前點\(i\)選還是不選。
- 而動態\(dp\)的思想就是,把\(dp\)方程寫成矩陣乘法的形式,然后用數據結構來維護區間矩陣乘積。
首先是樹鏈剖分的思想
- 講一講自己的感想
- 首先我們為了使得\(f\)帶修改,引入了樹鏈剖分套線段樹維護矩陣進行\(dp\)。
- 但是這樣我們是無法直接維護\(f\)的,因為剖分的思想是重鏈不會超過\(log\)次划分。
- 假設我們現在可以一次性跳完整個重鏈,那么問題就變成了是否可以從重鏈頂跳到上一個重鏈
- 於是我們引入新數組\(g\),表示不算當前重鏈的所有貢獻。
- 那么我們只需要維護\(g\),每次修改就直接把整個重鏈的\(g\)全部\(\prod\)起來,然后修改父親的\(f\)。
- 此時我們發現,不是每個地方的\(f\)都是有意義的。
- 首先我們知道每個位置的\(g\),然后用線段樹維護他。
- 維護\(g\)的原因是我們可以通過線段樹優化\(dp\)轉移使得在\(log\)的時間內得到一段的\(g\)卷積。
- 所以我們只知道每個鏈頂的\(f\)值,而並不關心每個點的\(f\)。
- 那么問題就簡單多了,對於每次修改,我們只需要修改當前點的\(g\),然后\(\prod\)到鏈頂的\(f\),再用當前的\(f\)更新父親的\(g\),重復這個操作。
- 實際上我們又給出了\(f\)的新定義,即\(f=\prod g\).
- 總結一下就是:
- 個人認為動態\(dp\)的思想在於整體考慮一整個重鏈的\(dp\)轉移。
- 然后再把這個重鏈的\(dp\)轉移貢獻給鏈頂父親
- 那么在實際設計狀態的時候,形如\(g_{i,j}\)表示\(i\)點不考慮重兒子的\(dp\)值。
- 然后根據狀態轉移方程一次性考慮一整個重鏈
- 這樣的話,我們在修改操作的時候,就只需要考慮鏈與鏈之間的關系了。
- 至此,我們得到了一個\(O(4*q*log^2n)\)的做法。
但是這樣還是不夠優秀
- 我們選擇更加優秀的\(lct\)維護矩陣信息。
- 優秀博客
- 不會lct來這里moflash
- 此時的\(f\)含義為\(splay\)整個的\(g\)卷積,\(g\)表示虛樹\(dp\)和。
- 那么\(splay\)就維護了整個實鏈的\(dp\)值。
- 重點說\(access\)……
- 轉到根,換兒子,更新信息,前操作點切換為輕邊所指的父親,轉1。
- 因為除此之外全是模板。
- 假設當前實鏈頂端結點是\(y\),它爹是\(x\),我們要做的就是把\(x\)在\(splay\)上的右兒子設置為\(y\)。這對於我們維護的虛子樹信息而言有兩個影響:
- 原來\(x\)的右兒子變成了虛的,相當於多了一棵虛子樹。
- \(y\)由虛的變成了實的,相當於少了一棵虛子樹。
- 另外這個\(LCT\)有點特別:它既不\(L\)也不\(C\),所以我們不需要翻轉標記,不需要\(pushdown\),不需要\(makeroot\)什么什么的。
- 至此,我們得到了一個\(O(4*q*logn)\)的做法。
- 至於為什么復雜度更加優秀,是因為樹剖要一條鏈一條鏈往上,一邊改一邊查,而\(LCT\)只要簡單粗暴地\(access\)一下即可。
- 在區間問題中,樹剖加線段樹是將鏈進行分治從而提取了很多段區間分別分治,但是\(lct\)和\(splay\)的優勢在於可以動態快速提取出整個區間。
- \(lct\)相比於樹剖,其實是避開了鏈分治的過程,也就省去了一個\(log\)的時間復雜度。
- 至於實現過程,我們一開始不需要建所有的\(splay\),可以認為一個\(splay\)就只有一個點本身,也就是所有的邊都是虛的。
但是問題在於,我好像忘了\(lct\)怎么打了
。。。。。
先咕咕,等停課了再說。