筆者最近意外的發現 筆者的我的網站 http://tiankonguse.com/ 的不少文章被其它網站轉載,可是轉載時未聲明文章來源或參考自 http://tiankonguse.com/ 網站,所以,筆者添加此條聲明。php
鄭重聲明:這篇記錄《【百度之星2014~複賽 解題報告~正解】The Query on the Tree》轉載自 http://tiankonguse.com/的這條記錄:http://tiankonguse.com/record/record.php?id=674git
昨天寫了 The Query on the Tree 的解題報告,可是遺留下一個問題,不能算是完美解決這道題.github
由於若是精心構造數據的話,昨天的題解仍是會被卡住的.數組
今天中午睡覺的時候忽然想起一個不會被卡住的方法.優化
可是因爲早上玩了一會相似與寵物消消的弱智遊戲,因而怎麼也停不下來了.網站
一個下午的時光也浪費在了這個弱智遊戲上.spa
到了晚上,手機終於沒電了,因而來寫寫這道題的完美解決方法.遊戲
這樣不管怎麼構造數據,tiankonguse都不用擔憂程序超時了.get
有一棵樹,樹的每一個點有點權,每次有三種操做: 1. Query x 表示查詢以x爲根的子樹的權值和。 2. Change x y 表示把x點的權值改成y(0<=y<=100)。 3. Root x 表示把x變爲根。 如今度度熊想請更聰明的你幫助解決這個問題。
這篇記錄和昨天那一篇緊密相連,建議看看那個記錄.it
傳送門(http://tiankonguse.com/record/record.php?id=673)
對於這道題,首先須要對樹按1爲根優先編號.
編號的時候記錄子樹的權值和以及子樹的編號範圍.
這樣設置根的通常複雜度是O(1), 修改的通常複雜度是O( log( n ) ), 查詢的通常複雜度也是 O( log( n ) ).
修改的最壞複雜度是O( n ), 咱們可使用線段樹來優化到O( log( n ) ).
對於查詢分了三部分,其中有一部分最壞狀況下複雜度也是 O( n ).
當時往二分優化上想了,可是目前的信息不知足二分的條件,因此二分不了.
假設目前查詢的是x, root 是根, y 是x的某個兒子, root 在 y 的那個子樹上.
咱們要搜索y這個節點.
咱們要搜的區間是 left[x]到 left[root], 其中 left[x] 最小,left[y] 不能肯定在那個地方,也不知道 left[y] 的值.
歐拉序列是什麼呢?
原來歐拉序列也對樹dfs編號了,只不過進入每一個兒子的時候都對當前子樹根編號,最後結束時再遍一次號,儲存的信息貌似很豐富.
貌似能夠.
由於這時x的每一個兒子前面必定有一個編號是x.
而咱們須要的是 root 前面的第一個 x.
又因爲 x 是區間內最小值,因此經過二分這個最小值就能夠搜到 y 了.
因爲須要二分,因此最少是 O( log( n ) ).
每次都須要判斷,因此這個咱們須要經過線段樹來優化,能夠優化到 log( n ).
這樣綜合複雜度就是 O( log( n ) ^ 2 )
這個固然能夠,就是一個二分加線段樹.
針對昨天遺留下的問題,這裏簡單的總結一下解決方法.
遺留的問題是查詢的時候,若是root是查詢節點x的子孫時,咱們須要找到x的某個兒子y,這個兒子y仍是root的祖先.
這個查找過程用昨天的方法最壞複雜度是O( n ) 的.
這裏我找到一個方法:對樹dfs編號的時候,每次在兒子前面都添加一個根節點,即把用根節點把各個兒子爲根的子樹分開.
這樣咱們就可使用二分查找x的兒子y了.
由於root前面第一個編號爲x的節點和y前面第一個編號爲x的節點是相同的.
並且第一個編號爲x的節點的下一個節點就是y節點.
因爲x仍是整個區間的最小值,因此咱們就能夠經過二分區間最小值來找到root前面的第一個編號爲x的節點了.
二分優化查詢(其餘的暴力的代碼)https://github.com/tiankonguse/ACM/blob/master/astar/2014/3/2.3.cpp
完整版的代碼(兩個線段樹寫爲一個了):https://github.com/tiankonguse/ACM/blob/master/astar/2014/3/2.4.cpp
參考
無