周二週三真的太難了,有早課導致不能熬夜,於是就只能趁著中午的時間寫一寫,這幾天先寫點簡單的東西,就當重新複習了,應該算是給初學者的知識普及,其他的過了週三再說。
首先來講一下樹的重心。
樹的重心,即 樹上到所有點的距離之和最小/以此為根深度最小/最大子樹大小最小 的點,具有很多方便的性質,如:
1.當一棵樹新增/刪除乙個節點,樹的重心最多移動乙個位置。(動態維護)(19icpc徐州m題,so~no~chi~no~sa~da~me~)
2.當兩棵樹通過某點連線在一起形成新樹時,新樹的重心一定在連線兩棵舊樹重心的路徑上。(兩樹合併)
3.以一顆樹的重心為根,劃分的子樹大小一定不超過原樹的一半。(樹分治)
眾所周知,回字有四種寫法,門前有兩棵棗樹,重心也有兩種求法(通常情況下),一種是兩遍dfs找樹上最長路徑的中點,一種就是今天要講的內容——樹形dp之換根法。
首先,初始是不知道重心所在的,最直接的方法就是列舉每個點作為重心的情況。由上面重心的定義可以知道,重心一定是最大的子樹大小最小的點。因此,我們不妨先把1
11作為根,設s[i
]s[i]
s[i]
為以1
11為根時子樹i
ii的大小,根據dfs的性質,子樹遍歷結束的時候,其s[i
]s[i]
s[i]
也被更新完了,所以,一次從1
11開始的dfs就可以求出所有點的子樹大小。
如果我們用f[i
]f[i]
f[i]
來表示以i
ii為根的最大子樹大小,根據上述演算法,dfs結束後可以求出f[1
]=ma
x(s[
son]
)f[1]=max(s[son])
f[1]=m
ax(s
[son
])。此時,如果根從1
11換到了1
11的子節點上,f[i
]f[i]
f[i]
該如何變化呢?
f [i
]=ma
x(s[
son]
,n−s
[i])
f[i]=max(s[son],n-s[i])
f[i]=m
ax(s
[son
],n−
s[i]
)這裡滿足了動態規劃的三要素:狀態、邊界條件、轉移方程。
更具體地說,要在樹上所有的點中找到滿足條件的根,就要確定隨根變化的資料(狀態)、初始當根為1
11的情況(邊界條件)、根從上往下移動的變化**移方程)。
來一道題感受一下:
cf1187e tree painting
題意:給定一棵所有結點初始為白色的樹,第一回合選擇任意乙個白點塗黑,接下來每次都選和黑點鄰接的白點塗黑,每次(包括第一次)選點時會獲得這個點所在的白點連通塊大小的分數,求可能獲得的最大分數。
這就是換根法的直接應用了。第乙個塗黑的點就相當於選乙個根,然後沿著根往下走。假設以1
11為根,s[i
]s[i]
s[i]
表示i
ii的子樹大小,1
11為根時的答案ans
1ans_1
ans1
即為∑i=
1ns[
i]
\sum_^ns[i]
∑i=1n
s[i]
。當根由u
uu變為v
vv時,ans
v=an
su−s
[v]+
(s[1
]−s[
v])=
ansu
−2∗s
[v]−
s[1]
ans_v=ans_u-s[v]+(s[1]-s[v])=ans_u-2*s[v]-s[1]
ansv=
ansu
−s[
v]+(
s[1]
−s[v
])=a
nsu
−2∗s
[v]−
s[1]
。a ns
ians_i
ansi
的最大值就是答案。
#include
#define n 200010
#define ll long long
using
namespace std;
int n;
vector<
int> g[n]
;ll sum[n]
,ans,s;
void
dfs1
(int u,
int fa)
s+=sum[u];}
void
dfs2
(int u,
int fa,ll s)
intmain()
dfs1(1
,0);
dfs2(1
,0,s);
cout<}
睡眠時間不夠,ddl到了作業也沒做,導致這次寫的比較倉促,內容也不是很詳盡,還好演算法並不複雜。明、後兩天準備寫一寫數論基礎,尤其是數學角度的推理過程(好像我也不怎麼擅長這些)。 每日演算法 計數DP和遞推DP
計數pd就是emm感覺求那種什麼路徑和的就是計數那一類的,再概括一下就是可以不用其他操作直接將它相鄰或者說符合要求的 求和,就是下乙個位置的值。回到這題 用動規很好做。我們想要知道 0,0 到終點的位置的路徑,只需要知道終點已左終點已下的路徑為多少就行了。dp i j 0,0 到 i,j 位置的路徑...
演算法訓練 結點選擇 樹形DP
問題描述 有一棵 n 個節點的樹,樹上每個節點都有乙個正整數權值。如果乙個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?輸入格式 第一行包含乙個整數 n 接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。接下來一共 n 1 行,每行描述樹上的一條邊。...
每日一題 POJ 2486 樹形dp 樹上揹包
並不難寫,關鍵是要想清楚。樹形dp的第乙個比較完整寫完的題,是比較經典的題,來紀念一下。一開始沒想清楚是因為對揹包問題的滾動陣列優化的理解不夠,自然在樹上的問題也沒思考清楚。同時因為每個case過後忘記清零,導致re了一發,以後需要注意。dpr是回到該點,nr是不回到該點,轉移方程如 ac 耗時18...