[NOI2014]購票 --- 斜率優化 + 樹形DP + 數據結構

[NOI2014]購票

題目描述

今年夏天,NOI在SZ市迎來了她30週歲的生日。git

來自全國 n 個城市的OIer們都會從各地出發,到SZ市參加此次盛會。github

全國的城市構成了一棵以SZ市爲根的有根樹,每一個城市與它的父親用道路鏈接。算法

爲了方便起見,咱們將全國的 n 個城市用 1 到 n 的整數編號。其中SZ市的編號爲 1。工具

對於除SZ市以外的任意一個城市 v,咱們給出了它在這棵樹上的父親城市 fv 以及到父親城市道路的長度 sv。優化

從城市 v 前往SZ市的方法爲:選擇城市 v 的一個祖先 a,支付購票的費用,乘坐交通工具到達 a。spa

再選擇城市 a 的一個祖先 b,支付費用併到達 b。以此類推,直至到達SZ市。code

對於任意一個城市 v,咱們會給出一個交通工具的距離限制 lv。blog

對於城市 v 的祖先 a,只有當它們之間全部道路的總長度不超過 lv 時,從城市 v 才能夠經過一次購票到達城市 a,不然不能經過一次購票到達。遞歸

對於每一個城市 v,咱們還會給出兩個非負整數 pv,qv 做爲票價參數。隊列

若城市 v 到城市 a 全部道路的總長度爲 d,那麼從城市 v 到城市 a 購買的票價爲 d * pv+qv。

每一個城市的OIer都但願本身到達SZ市時,用於購票的總資金最少。

你的任務就是,告訴每一個城市的OIer他們所花的最少資金是多少。

 

輸入輸出格式

輸入格式:

第 1 行包含2個非負整數 n,t,分別表示城市的個數和數據類型(其意義將在後面提到)。

輸入文件的第 2 到 n 行,每行描述一個除SZ以外的城市。

其中第 v 行包含 5 個非負整數 f_v,s_v,p_v,q_v,l_v,分別表示城市 v 的父親城市,它到父親城市道路的長度,票價的兩個參數和距離限制。

請注意:輸入不包含編號爲 1 的SZ市,第 2 行到第 n 行分別描述的是城市 2 到城市 n。

 

輸出格式:

輸出包含 n-1 行,每行包含一個整數。

其中第 v 行表示從城市 v+1 出發,到達SZ市最少的購票費用。

一樣請注意:輸出不包含編號爲 1 的SZ市。

 

輸入輸出樣例

輸入樣例#1:
7 3 1 2 20 0 3 1 5 10 100 5 2 4 10 10 10 2 9 1 100 10 3 5 20 100 10 4 4 20 0 10 
輸出樣例#1:
40 150 70 149 300 150

說明

從每一個城市出發到達 SZ的路線以下(其中箭頭表示一次直達):

城市 2:只能選擇 2 → 1,花費爲 2 × 20 + 0 = 40。

城市 3:只能選擇 3 → 1,花費爲 5 × 10 + 100 = 150。 城 市 4 : 由 於 4 + 2 =6 ≤ l4 = 10,故能夠選擇 4 →1。

若選擇 4 → 1,花費爲 (4 +2) × 10 + 10 = 70 ; 若選 擇 4 → 2 → 1,則花費爲 (4 ×10 + 10) + (2 × 20 + 0) =90;所以選擇 4 → 1。

城市 5:只能選擇 5 →2 → 1 , 花費爲 (9 × 1 +100) + (2 × 20 + 0) = 149;沒法選擇 5 → 1,由於 l5 =10,而城市 5 到城市 1 總路程爲 9 + 2 = 11 > 5,城市 5 不能直達城市 1。

城市 6:若選擇 6 → 1,花費爲 (5 + 5) × 20 + 100 = 300;若選擇 6 → 3 →1,花費爲 (5 × 20 + 100) + (5 × 10 + 100) = 350;所以選擇 6 → 1。

城市 7:選擇 7 → 4 → 1,花費爲 (4 × 20 + 0) + ((4 + 2) × 10 + 10) = 150;

其餘方案均比該方案差。

 

數據規模

 

 

 

首先,你要學會DP

記\(dp(i)\)表示\(i\)節點到達首都的最小費用

那麼,有轉移方程:

\(dp(i)=min(dp(j)+p(i)*dis(i,j)+d(i))\)

考慮轉化成斜率優化:

記\(dep(i)\)表示\(i\)節點到根的深度

\(dp(i)=dp(j)+p(i)*(dep(i)-dep(j))+d(i)\)

\(dp(i)=dp(j)+p(i)*dep(i)-p(i)*dep(j)+d(i)\)

\(dp(j)=p(i)*dep(j)+dp(i)-d(i)-p(i)*dep(i)\)

那麼,如今就是一個斜率優化的式子了。

其中,\(x\)是\(dep(j)\),單調遞增

其中,\(k\)是\(p(i)\),沒有任何單調性

只不過題目中有\(l(i)\)這個距離限制。

嗯。。。。怎麼辦?

 

凸包是不支持刪除的。

所以咱們要另闢蹊徑。

 

作法一:

樹鏈剖分

直接上樹剖後,發現每次查詢的凸包都對應原樹中的一條鏈。

那麼,咱們讓樹剖後的線段樹區間\(l...r\)中存下點\(dfn[l]...dfn[r]\)所造成的凸包。

對於每一個點,每次會查到\(\log n\)條重鏈,每次重鏈要查詢\(\log n\)個區間,每次區間中在凸包上三分須要\(\log n\)的時間

所以,總耗時\(O(n* (\log n)^{3})\)

過不去,不存在的,樹剖這種東西怎麼可能想卡就卡!!!

補充:常數優化:

每次線段樹中的凸包合併時,由於一個區間的全部元素沒有都被訪問過期,就必定不會查到這段區間。

所以,能夠選擇在左右兩個子區間的全部元素都被訪問過期,選擇\(O(n)\)的合併凸包

 

時間複雜度:\(O(n*(\log n)^{3})\)

空間複雜度:\(O(n* \log n)\)

 

作法二:

可持久化線段樹+平衡樹

樹剖的理論複雜度過不去???

若是你真的對理論複雜度有需求,那麼不妨來聽下如下作法。

發現,其實咱們只須要對當前\(dfs\)鏈維護一棵線段樹,這樣子,查詢的複雜度能降到\(O((\log n)^{2})\)

可是,爲了不元素過多(也能夠說發現父親的元素同本身變化不大),所以能夠用可持久化來維護。

 

時間複雜度:\(O(n* (\log n)^{2})\)

空間複雜度:\(O(n *(\log n)^{2})\)

 

作法三:

可持久化線段樹+單調隊列

樹套樹常數大??

不要緊,這題有一個很是重要的特殊性質:\(x\)單增

所以,咱們只須要一個單調隊列就能夠表示一個凸包了。

具體而言,能夠選擇用線段樹來實現這個隊列。

然而,這題還有特殊性:鏈都是父親來的,也就是說咱們只須要訪問特定的版本。

對於上個版本,咱們只須要記錄新插入的點替代了誰。

固然這樣子,插入時要二分插入的位置。

 

時間複雜度:\(O(n* (\log n)^{2})\)

空間複雜度:\(O(n * \log n)\)

 

作法四:

點分治+可持久化平衡樹

首先套一個點分,思考點份內部幹什麼。

很天然地,想到把祖先所在的鏈找出來,更新下面全部的子樹。

爲何正確呢?

證實一下兩個結論:

1.全部祖先節點優先被更新

這準確來講並非一個結論,但它告訴咱們,先遞歸祖先所在的子樹,再處理本身

2.一個點能夠被全部的祖先更新

若是當前分治結構還有祖先無法更新,那麼這些祖先會在下層分治結構中成爲更新點從而更新當前節點。

所以,點分是沒有問題的。

如何知足限制?

把全部的祖先建出一棵可持久化平衡樹,在上面查詢便可。

 

作法五:

點分治+CDQ分治

把上面的可持久化平衡樹用CDQ分治來替代便可

 

作法六:

點分治+三分

把全部凸包按順序加入,全部點按順序在上面三分

 

代碼在此

 

作法七:

點分治+亂搞

莫名其妙的亂搞。

複雜度最壞\(O(n^{2})\),不知爲什麼能過,並且很快。。。

 

代碼在此

 

分治算法的時間複雜度爲\(O(n * (\log n)^{2})\)

空間複雜度爲\(O(n)\)

是最優的複雜度

 

補充:

當你的算法涉及\(O(n)\)建凸包時,請當心處理斜率的關係

相關文章
相關標籤/搜索