很典型的按照邊考慮貢獻的題。小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 longendlong
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
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...