生活随笔
收集整理的這篇文章主要介紹了
树链剖分-学习报告
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
概要
(這是筆者剛學完趁熱寫的博客
由于也是初學,所以可能會對初學讀者比較友好吧
畢竟你不明白的地方我可能也迷惑過)
學完感覺樹鏈剖分的設計真的很巧妙
其主要用途是壓縮樹上路徑
又是一個n到log的優化
原理
先引定義:
設定每個結點所有兒子中,子樹的結點個數最多的兒子為重兒子
重兒子連成的鏈叫做重鏈
每條重鏈深度最小的點叫做重鏈的鏈頭
能得出一個很重要的結論:
從根出發的任意一條路徑經過的重鏈數不超過logm(m為邊數)
這是樹鏈剖分提速的關鍵
這個結論畫個圖就能很容易的看出來(路徑產生斷點必然意味著有一條至少比原路徑結點更多的子樹)
應用
樹鏈剖分常常和dfs序結合
如果再dfs時先dfs重兒子,重鏈的dfs序就是連續的
我們就可以一起進行修改
這樣就可以用線段樹之類的結構進行維護了
實現
那么接下來我們嘗試代碼實現
dfs
先樹上dfs一遍求出每個結點的深度、父親、子樹大小等基本信息
順便把重兒子也求了
void dfs1(int x
,int f
){size
[x
]=1;for(int i
=fi
[x
];~i
;i
=p
[i
].nxt
){int to
=p
[i
].to
;if(to
==f
) continue;dep
[to
]=dep
[x
]+1;fa
[to
]=x
;dfs1(to
,x
);size
[x
]+=size
[to
];if(!hson
[x
]||size
[to
]>size
[hson
[x
]]) hson
[x
]=to
;}return;
}
再dfs一遍求dfs序和鏈頭
注意先dfs重兒子!
void dfs2(int x
,int tp
){top
[x
]=tp
;dfs
[++tot
]=x
;pos
[x
]=tot
;a
[tot
]=b
[x
];if(!hson
[x
]) return;dfs2(hson
[x
],tp
);for(int i
=fi
[x
];~i
;i
=p
[i
].nxt
){int to
=p
[i
].to
;if(to
==fa
[x
]) continue;if(to
==hson
[x
]) continue;else dfs2(to
,to
);}return;
}
(dfs數組和pos數組互相映射,按需取食)
修改&查詢
dfs之后就要開始干活了
修改和查詢其實一個道理
兩個點如果鏈頭相同,就說明在同一條重鏈上
直接對兩者之間的dfs區間處理即可
否則就選深度大的那個點,讓它往上跳一條鏈,同時對這條鏈處理
直到兩點在同一條重鏈上為止
對區間的修改查詢當然離不開強大的線段樹啦
void roadchange(int x
,int y
,int v
){while(top
[x
]!=top
[y
]){if(dep
[top
[x
]]<dep
[top
[y
]]) swap(x
,y
);longchange(1,1,tot
,pos
[top
[x
]],pos
[x
],v
);x
=fa
[top
[x
]];}if(dep
[x
]<dep
[y
]) swap(x
,y
);longchange(1,1,tot
,pos
[y
],pos
[x
],v
);return;
}
ll
roadask(int x
,int y
){ll ans
=0;while(top
[x
]!=top
[y
]){if(dep
[top
[x
]]<dep
[top
[y
]]) swap(x
,y
);ans
+=longquery(1,1,tot
,pos
[top
[x
]],pos
[x
]);
ans
%=mod
;x
=fa
[top
[x
]];}if(dep
[x
]<dep
[y
]) swap(x
,y
);ans
+=longquery(1,1,tot
,pos
[y
],pos
[x
]);
return ans
%mod
;
}
線段樹
不知道怎么寫就點這里吧
細節
這是筆者第一次寫的時候遇到的一些小的問題
與你共勉吧
1.線段樹訪問時應該是dfs序小的寫在前,也就是深度小的在前!
2.踩一個坑已經足夠,所以沒有2了
thanks for reading!
總結
以上是生活随笔為你收集整理的树链剖分-学习报告的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。