烟花表演是最引人注目的節日活動之一。在表演中,所有的烟花必須同時**。為了確保安全,烟花被安置在遠離開關的位置上,通過一些導火索與開關相連。導火索的連線方式形成一棵樹,烟花是樹葉,如圖 1所示。火花從開關出發,沿導火索移動。每當火花抵達乙個分叉點時,它會擴散到與之相連的所有導火索,繼續燃燒。導火索燃燒的速度是乙個固定常數。圖 1展示了六枚烟花 \(\\) 的連線布局,以及每根導火索的長度。圖中還標註了當在時刻 \(0\) 從開關點燃火花時,每一發烟花的**時間。
圖 1hyunmin 為烟花表演設計了導火索的連線布局。不幸的是,在他設計的布局中,烟花不一定同時**。我們希望修改一些導火索的長度,讓所有烟花在同一時刻**。例如,為了讓圖 1中的所有烟花在時刻 \(13\) **,我們可以像圖 2中左邊那樣調整導火索長度。類似地,為了讓圖 1中的所有烟花在時刻 \(14\) **,我們可以像圖 2中右邊那樣調整長度。
圖 2修改導火索長度的代價等於修改前後長度之差的絕對值。例如,將圖 1中布局修改為圖 2,左邊布局的總代價為 \(6\),而將圖 1中布局修改為圖 2右邊布局的總代價為 \(5\)。
導火索的長度可以被減為 \(0\),同時保持連通性不變。
給定乙個導火索的連線布局,你需要編寫乙個程式,去調整導火索長度,讓所有的烟花在同一時刻**,並使得代價最小。
所有的輸入均為正整數。令 \(n\) 代表分叉點的數量,\(m\) 代表烟花的數量。分叉點從 \(1\) 到 \(n\) 編號,編號為 \(1\) 的分叉點是開關。烟花從 \(n+1\) 到 \(n+m\) 編號。
\(n\:\:m\)
\(p_2\:\:c_2\)
\(p_3\:\:c_3\)
\(\ldots\)
\(p_n\:\:c_n\)
\(p_\:\:c_\)
\(\ldots\)
\(p_\:\:c_\)
其中 \(p_i\) 滿足 \(1\le p_i,代表和分叉點或烟花 \(i\) 相連的分叉點。\(c_i\) 代表連線它們的導火索長度 \((1\le c_i\le 10^9)\)。除開關外,每個分叉點和多於 \(1\) 條導火索相連,而每發烟花恰好與 \(1\) 條導火索相連。
子任務 1(7 分):\(n=1,1 \le m \le 100\)。
子任務 2(19 分):\(1 \le n+m \le 300\),且開關到任一烟花的距離不超過 \(300\)。
子任務 3(29 分):\(1 \le n+m \le 5000\)。
子任務 4(45 分):\(1 \le n+m \le 3\times 10^5\)。
\(\\\)
參考部落格
設\(f_i(x)\)為使得\(i\)的子樹中所有葉子到\(i\)距離為\(x\)所付出的代價。很明顯\(f_i(x)\)是個下凸的函式,而且中間有一整段斜率為\(0\)的區間,假設為\([l,r]\)。我們先考慮已經得到了\(f_u(x)\),要加上\(u\)的父親到\(u\)的那條邊(長度為\(w\))後函式怎麼變化。
\[f_(x)=
\begin
f_u(x)+w & x\leq l\\
f_u(l)+w-(x-l) & l\leq x\leq l+w\\
f_u(l) & l+w
假設最終這條邊的邊權為\(w'\),最優策略就是盡量使得\(x-w'\),也就是所有葉子到\(u\)的距離盡量往\([l,r]\)靠。
我們觀察這個轉移,相當於將\(l\)以左的部分抬高\(w\),然後接一條斜率為\(-1\)的直線,然後接一條斜率為\(0\)的直線,最後接斜率為\(1\)的直線。也就是說先將右端的斜率大於\(0\)的直線全部刪除,再加入上述三條直線。
假設我們已經完成了這個操作,於是就把這個函式加到\(fa_u\)的函式中。我們發現累加函式會使斜率逐漸增大。比如函式\(1\)在\(x\geq x_1\)的部分斜率為\(1\),函式\(2\)在\(x\geq x_2\)的部分斜率為\(1\)(\(x_1),那麼新函式在\(x_1\leq x\leq x_2\)的部分斜率為\(1\),在\(x>x_2\)的部分斜率為\(2\)。
所以我們只需要維護函式的拐點的橫座標就行了。考慮每合併乙個兒子,函式的最大斜率都會\(+1\),所以函式最右端斜率\(>0\)的端點個數就是兒子的數量。具體實現可以用可並堆。
最後考慮得到最終的函式後怎麼算答案。明顯答案就在那一段斜率為\(0\)的區間,但是我們只維護了橫座標。我們知道\(f_1(0)=sum\),其中\(sum\)表示所有的邊長度之和。每次合併函式,最大斜率\(+1\),同理最小斜率也會\(-1\),所有函式左側的斜率也是遞減的。假設最左側斜率\(<0\)的一堆端點有\(k\)個,分別為\(x_1,x_2,\ldots,x_k\),那麼答案就
\[sum-x_1*k+\sum_^k(x_i-x_)*(k-i+1)\\
=sum-\sum_^kx_i
\]**:
#include#define ll long long
#define n 600005
using namespace std;
inline int get() while('0'<=ch&&ch<='9') return x*f;}
int n,m;
struct tree tr[n<<1];
int merge(int a,int b)
tr[++tot].val=l+len[i];
tr[++tot].val=r+len[i];
rt[i]=merge(rt[i],merge(tot-1,tot));
rt[fa[i]]=merge(rt[fa[i]],rt[i]); }
while(sn[1]--) rt[1]=pop(rt[1]);
while(rt[1])
cout<
return 0;
}
APIO2016 煙火表演
1.x l f x f x w 2.l x l w f x f l w x l 3.l w x r w f x f l 4.r w x f x f l x r w 意思是當前節點考慮到父親的那條邊權為w的邊對於不同x取值的轉移,l,r 表示最小代價的左右端點,而因為在葉子節點初始化只有乙個點 實際上...
APIO2016 烟花表演
解題思路 又是一道solpe trick題,觀察出影象變化後不找一些性質還是挺難做的。首先令 dp u i 為節點 u 極其子樹所有葉子到 u 距離為 i 的最少代價,顯然有 dp u i sum min 定義函式 f u x dp u x g u x min 可以得到 f u x sum g v ...
loj2587 APIO2018 鐵人兩項
建立圓方樹 考慮我們要求解的其實是 圓方樹上任意兩個圓點之間的路徑和 當然方點的點權就是e這個點雙包含的點的個數 為了去重圓點的權值就是 1 我們考慮如何計算這樣 任意兩點的權值和 我們不妨假設 我們列舉每個中間點 然後看他會對多少對圓點產生貢獻即可 就在樹上跑一下 注意在圓點的時候 因為這個圓點的...