演算法難度5,思維難度7,**難度6
給定一棵n
nn個點的完全二叉樹,樹邊帶權,點帶val
(x
)val(x)
val(x)
的權值。
定義兩個點的距離dis
(u,v
)dis(u,v)
dis(u,
v)為u
uu到v
vv的最短路徑上經過的邊權之和。
你一開始可以選擇乙個起始點,並點亮這個點,這一步不需要花費。
你在點亮乙個點之後,必須緊接著將它的子樹內所有點都點亮,才可以點亮其他點。
同時你需要滿足點亮的點時刻在乙個連通塊內。
除了第一步,你點亮其他點所需的代價為dis
(u,v
)×va
l(v)
dis(u,v)\times val(v)
dis(u,
v)×v
al(v
),其中u
uu是你上一次點亮的點,v
vv是你這次點亮的點。
1 ≤n
≤2×1
05,1
≤val
≤105
1\le n\le 2\times 10^5,1\le val\le 10^5
1≤n≤2×
105,
1≤va
l≤10
5我們考慮如何dp這個東西。
首先要考慮如果確定乙個起點,答案會是什麼樣子的。
答案一定是:先每次選擇乙個兒子,填完這棵子樹,再出來填另一棵;填完這個點為根的子樹後,向父親走,再去填另一棵子樹,這樣往復直到填完整棵樹。
那麼我們來考慮如何用設計dp狀態,使得能用這個dp來湊出答案來。
假如我們設dp(
x)
dp(x)
dp(x
)表示填完x
xx的子樹所需的最小代價呢,顯然這東西沒法轉移,因為你不知道你從哪兒來。
假如來個dp(
i,j)
dp(i,j)
dp(i,j
)表示填完i
ii的子樹且最後停在j
jj的最小代價呢,顯然狀態數太過龐大。
我們換個思路,既然沒法記錄從哪兒來,不如記錄我們下一步去哪兒?
我們用dp(
i,j)
dp(i,j)
dp(i,j
)表示填完i
ii的子樹,且最後去點亮j
jj的最小代價,發現合法狀態數非常少,是o(n
logn
)o(nlogn)
o(nlog
n)級別的。
因為每個點填完子樹之後,下一步只能點亮乙個祖先或者祖先的另乙個兒子。
具體來定義一下這個dp狀態吧:dp(
i,j,
0/1)
dp(i,j,0/1)
dp(i,j
,0/1
)代表填完i
ii的子樹,0
00代表下一步填j
jj級祖先,1
11代表下一步填j+1
j+1j+
1級祖先的另乙個兒子,也就是j
jj級祖先的兄弟。
我們發現這個dp狀態可以涵蓋所有需要的資訊,轉移雖然有點兒複雜,但卻十分自然。
來具體說一下狀態轉移吧:
如果這個點是葉子,也就是它沒有兒子,那麼只需要直接計算跳躍的代價就好了。
如果這個點有左兒子,那麼只需要先走下去並計算這一步的代價,然後從左兒子跳到j+1
j+1j+
1級祖先即可。
如果這個點有左右兒子,那麼就對於先走左邊還是先走右邊分類討論,然後取min即可。
這樣我們就可以處理出來所有dp值了。
考慮有了這些dp值,我們如何計算最終答案。
考慮列舉從哪個點開始,然後模擬填的過程:先每次選擇乙個兒子,填完這棵子樹,再出來填另一棵;填完這個點為根的子樹後,向父親走,再去填另一棵子樹,這樣往復直到填完整棵樹。
然而這之中會有點兒小問題,就是走到根之後很難繼續走下去了,我們發現沒有合適的狀態供我們使用了。
我們可以新增乙個虛擬節點0
00號點,它的點權為0
00,它連一條向1
11的邊,權值為000。
這樣一切問題都可以完美地解決了,因為我們的策略總是最後走到0
00號點然後停下,然而0
00號點權值為0
00,對答案毫無影響。
這樣我們就可以用o(n
logn
)o(nlogn)
o(nlog
n)的時間複雜度來解決這個題了。
我思考了一下,覺得dp的一大關鍵就是如何用狀態拼出來結果,自己體會一下吧。
寫這個題的時候需要注意邊界問題。
#include
#include
#include
#include
#include
#include
#define ll long long
using
namespace std;
inline
intread()
while
(ch>=
'0'&& ch<=
'9')x=x*10+
(ch^48)
,ch=
getchar()
;return f==
1?x:
-x;}
const
int n=
2e5+5;
int n;
ll a[n]
,val[n]
;ll st[n][20
],dis[n][20
],dp[n][20
][2]
;inline
void
solve()
for(
int i=n;i>=1;
--i)
elseif(
(i<<1|
1)>n)
else}}
ll ans=
0x3f3f3f3f3f3f3f3f
;for
(int i=
1;i<=n;
++i)
ans=
min(ans,tmp);}
printf
("%lld\n"
,ans);}
intmain()
SCOI2015 小凸玩密室
題目描述 小凸和小方相約玩密室逃脫,這個密室是一棵有 n 個節點的完全二叉樹,每個節點有乙個燈泡。點亮所有燈泡即可逃出密室。每個燈泡有個權值 a i 每條邊也有個權值 b i 點亮第 1 個燈泡不需要花費,之後每點亮乙個新的燈泡 v 的花費,等於上乙個被點亮的燈泡 u 到這個點 v 的距離 d u,...
SCOI2015 小凸玩密室 解題報告
雖然有心裡在想一些奇奇怪怪的事情的原因,不過還是寫太久了.不過這個題本身也挺厲害的 注意第乙個被點亮的是任意選的,我最開始壓根沒注意到 dp 代表 i 號點子樹最後連出去的乙個點連的是它第 j 層的祖先 f 代表 i 號點子樹最後連出去的乙個點連的是它第 j 層祖先的另乙個兒子 然後我們就可以拼子樹...
SCOI2015 小凸玩矩陣
題目很明顯的乙個事情,求第 k 大的最小值,一般採用的做法是二分 有人會問,但是不滿足二分性啊,接著往下看 考慮二分答案,但是如何檢驗 mid 是對還是錯,考慮每次只能取 mid 的數字,如果能取到 n k 1 個數字以上就可以,至於看能否取到 n k 1 個,採用二分圖匹配,左邊的點是行,右邊的點...