樹形dp 7 14城市

2022-05-20 03:48:10 字數 1965 閱讀 1505

很典型的按照邊考慮貢獻的題。

小a居住的城市可以認為由n個街區組成。街區從1到n依次標號街區與街區之間由街道相連,每個街區都可以通過若干條街道到達任意乙個街區,共有n-1條街道。其中標號為i的街區居住了i名居民。居民會去拜訪別人,但是要花費dis(u,v)的過路費,u是他所在的城市,v是他拜訪的人所在的城市。你需要求出,所有人都拜訪其他人一次花費的過路費之和。

第一行乙個整數n

'>n

n接下來n-1行,每行2個整數n−1

'>n−1

n−1個整數描述n-1條街道

乙個整數,表示總花費之和

51 2

2 32 4

1 5184

對於30%的資料,滿足n

≤200

'>n≤200

n≤200n≤

200'>對於60%的資料,滿足n

≤3000

'>n≤3000

n≤3000n≤

200'>n

≤3000

'>對於100%的資料,滿足n

≤1000000

'>n≤1000000

n≤1000000

是一道典型的按邊統計答案的題。但為什麼我又沒想出來啊。

題目求的是∑u∑v∗dis(u,v),那麼來考慮一下問題的瓶頸在**。

首先是按照定義直接做的想法。

那麼統計列舉所有點對,是 o(n^2) 的,預處理 dis(u,v) 有 o(n^3) 的floyd;還有 o(n^2) 的做 n 次dfs。

然而這個方向的做法空間複雜度是肯定要 o(n^2) 的,而且統計列舉的複雜度也難以改進。

考試時候就是吊死在這顆樹上沒出來了……

統計時候不要那麼「直接」,而是把整個答案分部分來考慮。

對於每乙個點,與之相關的答案是 i*∑dis[i] 。於是我們發現最後的答案是只與 sum_ 有關的。也就是說,對於點 x ,如果預處理了以它為根的 dis ,那麼其貢獻就是可以 o(1) 求出的。因此,解題瓶頸從處理點對的 dis[u][v] 變為了轉移 dis[i] 。

對此,cptraser表示有一種神奇的「平衡移動」方法。

這裡(1,2)這條邊是正在列舉的邊。我們現在要做的是快速將 ∑dis(以1為根) 轉為 ∑dis(以2為根) 。

圖畫出來後就很顯然了。有

$\sum_ \qquad \quad =\sum_ \quad +tot_v-2*size[v]$

。其中 size[v] 表示以 v 為根子樹大小。當然這裡所謂的子樹大小是要提前人為確定乙個根節點的。

這般把答案分部分之後,我們就會驚喜地發現複雜度降為$o(n+m)$了。

1 #include2 typedef long

long

ll;3

const ll mo = 1e9+7;4

const

int maxn = 1000035; 5

const

int maxm = 2000035;6

7intn;8

intedges[maxm],nxt[maxm],head[maxn],edgetot;

9ll tmp,ans,sum,dis[maxn],size[maxn];

1011

intread()

1223

void addedge(int u, int

v)24

28void dfs1(int x, int

fa)29

35void dfs2(int x, int

fa)3647}

48ll qmi(ll a, ll b)

4956

return

ret;57}

58int

main()

59

end

2651 城市改建 樹形DP

我太sb了。一看輸出方案就瞎jb記錄了一坨資訊。最後發現根本沒有用。結果寫了6.7k。成功成為了bzoj寫的最長跑的最慢的選手2333。題目即在一棵樹上刪一邊加一邊,使得新樹的直徑最小。那麼我們就要維護直徑相關的資訊。於是大力dp。首先自底向上dp,設fi 表示以節點 i 為根的子樹的直徑,gi 表...

TJOI2017 城市 樹形dp

這是個神仙題,會卡常 題目讓你改一條邊把直徑變得最短。列舉每條邊,會把圖分成兩個地方,兩個連通塊 x區和y區域 都換根dp一下,算出離x最遠的點的距離記為dis x 然後列舉一下 新直徑有三個 1 max dis x x裡面最大的 2 dis y y裡面最大的 3 min dis x dis y l...

luogu1453 城市環路 樹形dp

p1453城市環路 最開始是按騎士那道題的做法 只是這道是雙向邊 先dfs一遍判環 根節點一定在環上 然後從根節點出發 強制不選根節點的父親 因為建的是雙向邊dfs來dp的時候會重複算 所以用vis來記錄這個點有沒有走過 然後再來一遍從根節點的父親出發 不選根節點的父親的父親 不知道為啥我第二遍dp...