同步
原題鏈結
簡要題意:
給定一棵樹,求最長的 「掛鏈」 長度。
掛鏈定義為:一條鏈上所有節點與其相連的節點構成的生成樹。(非嚴謹定義)(原題中是 「毛毛蟲」,本人以為掛鏈更形象)
這題有多種做法,這裡給出思路,以及其中一種做法的**。
注意到,其實我們只需要選出 「最長鏈」,然後在最長鏈的兩側掛鏈即可。
即,先求出樹的直徑的兩個端點,然後遍歷一遍直徑上的端點,把它們的直接連邊都加入生成樹中。
最後統計答案即可。
時間複雜度:o(n
)o(n)
o(n)
.期望得分:100pt
s100pts
100pts
.考慮樹形 dp
\text
dp,用 f
if_i
fi 表示以 i
ii 為根的最大掛鏈長度,sub
isub_i
subi
記錄 i
ii 的兒子個數,則:
f u=
max(f
v,fv
+sub
u−1)
(v
∈son(
u)
)f_u = \max(f_v , f_v + sub_u - 1) (v \in \operatorname (u))
fu=
max(fv
,fv
+su
bu−
1)(v
∈son
(u))
很顯然,要麼直接繼承兒子節點的答案,要麼把兩邊一拼。
時間複雜度:o(n
)o(n)
o(n)
.期望得分:100pt
s100pts
100pts
.給出乙個最巧妙的演算法。
假設 i
ii 號點的度是 a
ia_i
ai,則一條長度為 s
ss 的掛鏈的答案應該是:
( ∑i
∈lista
i)−(
s−1)
+1
\bigg ( \sum_} a_i \bigg ) -(s-1) + 1
(i∈lis
t∑a
i)−
(s−1
)+1i
∈list
i \in \operatorname
i∈list
表示 i
ii 屬於當前鏈,s−1
s-1s−
1 即把重複計算的邊去掉得到邊數,然後 +1+1
+1得到點數。化簡即:
= (∑
i∈
lista
i)−s
+2
= \bigg ( \sum_ } a_i \bigg ) - s + 2
=(i∈li
st∑
ai)
−s+2
你發現這個東西有點難看,於是換了一下:
= ∑i
∈list(
ai−1
)+
2=\sum_ } (a_i - 1) + 2
=i∈lis
t∑(
ai−
1)+2
顯然,把 s
ss 減到每個節點裡面 ,每個點分 1
11 個。
那麼我們可以按照如下步驟求解:
建圖統計度,並將度 −1-1
−1.求出直徑兩端。
統計直徑上所有節點的度,然後 +2+2
+2即為答案。
時間複雜度:o(n
)o(n)
o(n)
.實際得分:100pt
s100pts
100pts
.
#pragma gcc optimize(2)
#include
using
namespace std;
const
int n=
3e5+1;
inline
intread()
int x=0;
while
(isdigit
(ch)
) x=x*
10+ch-
'0',ch=
getchar()
;return x*f;
}int n,m,du[n]
,dis[n]
;int num,mx;
vector<
int> g[n]
;inline
void
dfs(
int dep,
int fa,
int dis)
intmain()
dfs(1,
0,du[1]
); mx=0;
dfs(num,
0,du[num]);
//兩次 dfs 尋找直徑
printf
("%d\n"
,mx+2)
;// +2 就是答案
return0;
}
HAOI2009 毛毛蟲 樹形dp
試題描述 對於一棵樹,我們可以將某條鏈和與該鏈相連的邊抽出來,看上去就象成乙個毛毛蟲,點數越多,毛毛蟲就越大。例如下圖左邊的樹 圖 1 抽出一部分就變成了右邊的乙個毛毛蟲了 圖 2 輸入資料 在文字檔案 worm.in 中第一行兩個整數 n m 分別表示樹中結點個數和樹的邊數。接下來 m 行,每行兩...
HAOI2009 毛毛蟲 樹形DP
題意 給你一棵樹,從樹中取出一部分滿足 是一條鏈 一些直接連在這條鏈上的節點 求節點數最多的合法取出部分。題解 其實這題還是不難?觀察到對於任意一條鏈,只有兩種情況 一條路走到底 or 以某個點為中轉 f x 表示從x往下走,一路走到底的包括x的最優解,f x 包括x也包括father x 將會加入...
haoi2009 毛毛蟲 樹形dp
這道題細節處理不少,但要ac不難 設以i節點為根節點的子樹能形成的最大的毛毛蟲長度為f i 則f i max f j i節點的孩子數 答案需要f最大和次大的兩個子樹合併,而且若合併的位置不是根節點,ans 我就是坑在了最後一點上,最後打表找到了問題 1 include2 include3 inclu...