樹鏈剖分入門詳解
樹鏈剖分入門詳解
以前沒有接觸過樹鏈剖分的同學們看到這個東西是不是覺得很高大上呢,下面我將帶你們進入樹的世界(講得不好別打我)
首先我們來看一道題
NOI2015D2T2軟件包管理器
題目描述如下
Linux用戶和OSX用戶一定對軟件包管理器不會陌生。通過軟件包管理器,你可以通過一行命令安裝某一個軟件包,然後軟件包管理器會幫助你從軟件源下載軟件包,同時自動解決所有的依賴(即下載安裝這個軟件包的安裝所依賴的其它軟件包),完成所有的配置。Debian/Ubuntu使用的apt-get,Fedora/CentOS使用的yum,以及OSX下可用的homebrew都是優秀的軟件包管理器。
你決定設計你自己的軟件包管理器。不可避免地,你要解決軟件包之間的依賴問題。如果軟件包A依賴軟件包B,那麽安裝軟件包A以前,必須先安裝軟件包B。同時,如果想要卸載軟件包B,則必須卸載軟件包A。現在你已經獲得了所有的軟件包之間的依賴關系。而且,由於你之前的工作,除0號軟件包以外,在你的管理器當中的軟件包都會依賴一個且僅一個軟件包,而0號軟件包不依賴任何一個軟件包。依賴關系不存在環(若有m(m≥2)個軟件包A1,A2,A3,?,Am,其中A1依賴A2,A2依賴A3,A3依賴A4,……,A[m-1]依賴Am,而Am依賴A1,則稱這m個軟件包的依賴關系構成環),當然也不會有一個軟件包依賴自己。
現在你要為你的軟件包管理器寫一個依賴解決程序。根據反饋,用戶希望在安裝和卸載某個軟件包時,快速地知道這個操作實際上會改變多少個軟件包的安裝狀態(即安裝操作會安裝多少個未安裝的軟件包,或卸載操作會卸載多少個已安裝的軟件包),你的任務就是實現這個部分。註意,安裝一個已安裝的軟件包,或卸載一個未安裝的軟件包,都不會改變任何軟件包的安裝狀態,即在此情況下,改變安裝狀態的軟件包數為0。
這是原地址
這道題的大意就是物品之間存在依賴與被依賴的關系,且不構成環,有一個物品無依賴物品。
我們定義一個物品及它所依賴的物品以及依賴的依賴等等為依賴鏈,依賴鏈包括這個物品的物品的集合為依賴子樹。
那麽放一個物品的同時需要放完整的依賴鏈,拿走一個物品的時候,也需要拿走完整的依賴子樹。
通過以上的分析,我們可以看出這大概是一個樹形結構。
由於節點個數和操作次數的範圍為\(1e5\),明顯要用一個\(nlgn\)或者\(nlg^2n\)的算法,那麽就可以引出我們今天的主角——樹剖了。
樹鏈剖分
顧名思義,樹鏈剖分就是指將一顆樹分成若幹條鏈,使得可以使用數據結構(例如線段樹,主席樹)來進行維護
它的特點很明顯,我們可以非常便捷的處理同一條鏈上的若幹點和邊
直說上面大概會讓大家一頭霧水,這只是讓大家對樹剖有一個初步的概念,下面我們要開始正式的講解了
先下若幹定義
重兒子:子樹最大的兒子
輕兒子:除了重兒子以外的所有兒子
重邊:父節點與重兒子組成的邊
輕邊:除重邊以外的邊
重鏈:重邊連接而成的鏈
輕鏈:輕邊連接而成的鏈
下圖為例
樹鏈剖分入門詳解