csdn同步
原題鏈結
簡要題意:
給定一棵樹,\(d_\) 為 \(x\) 與 \(y\) 距離(\(d_ = 0\)),選出兩個點 \(x,y\),最小化:
\[\sum_ (w_u \times \min(dis_ , dis_))
\]這種水的樹形dp 黑題,沒幾個人做真是太可惜了
首先我們要明白這個式子是什麼意思。
\(\min (dis_ , dis_)\),就是在 \(x\) 和 \(y\) 中找到較近的那乙個的距離。
\(w_u\) 就是點權,\(\sum_\) 是列舉所有節點。
即,所有節點的點權 \(\times\) 離 \(u,v\) 較小的距離之和。
那麼,如果只要求乙個 \(u\),就是樹的重心,也就是本題的弱化版:
p1364 醫院設定
那麼,現在變成了雙重心(其實重心比中心形象一點),怎麼做?
考慮乙個 \(o(n^2)\) 的做法。
顯然,對於任意一組 \(x,y\),會有乙個點集它們都離 \(x\) 較近,另乙個點集離 \(y\) 較近,這兩個點集的分界是一條邊。
那麼,我們只需要列舉斷邊(即將樹一分為二),形成點集,對兩邊的點集分別用重心模板求出,將答案之和取最小值。
總時間複雜度:\(o(n^2)\).
期望得分:\(0\) ~ \(100pts\).(出題人沒給部分分,洛谷評測機跑得快)
顯然,列舉斷邊無法優化,那我們考慮優化取重心。
下面我們要引出一些樹鏈剖分的知識。
乙個節點 \(i\),它所有兒子 \(u \in son_v\) 中,\(siz _ u\)(子樹權值和) 最大那個,我們稱之為重兒子,其餘是輕兒子。若干重兒子形成鏈是重鏈。
那麼,以 \(i\) 為根的子樹的重心,如果不在 \(i\),那麼,重心是在重兒子的子樹中,還是輕兒子的子樹中?
常識告訴我們,肯定是在重兒子的子樹中比較好啊。(讀者可自證)
所以,我們只需要初始化每個節點的重兒子編號即可。
但是有個問題:萬一我斷邊,正好把重兒子的邊斷掉了呢?
所以,我們還要處理每個節點的次重兒子,重兒子沒了的時候用次重兒子。
然後,我們只需要列舉重鏈上的點作為重心的答案即可。
時間複雜度:\(o(n \times h)\)(\(h\) 為樹高,因為重鏈長度 \(\leq h\),這也是就是題目明確說明 「樹高 \(\leq 100\)」 的用意所在啊)
實際得分:\(100pts\).
#pragma gcc optimize(2)
#includeusing namespace std;
const int n=5e4+1;
inline int read()
int x=0;while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return x*f;}
int val[n],cdson[n],zdson[n];
int ans=int_max,siz[n],f[n];
int w[n],n,dep[n],cut;
vectorg[n];
inline void dfs(int u,int fa)
}inline void getans(int u,int now,int all,int &res) //得到以當前節點為重心的答案
inline void solve(int u)
}int main() for(int i=1;i<=n;i++) w[i]=read();
dfs(1,0); solve(1);
printf("%d\n",ans);
return 0;
}
P2726 SHOI2005 樹的雙中心 題解
同步 原題鏈結 簡要題意 給定一棵樹,dx,yd dx,y 為 x xx 與 y yy 距離 dx,x 0d 0 dx,x 0 選出兩個點 x,y x,yx,y,最小化 u v wu min disx u,d isy,u sum w u times min dis dis u v wu min di...
SHOI2005 樹的雙中心
首先我們要知道,選擇兩個點 a,b 必定存在一條邊,割掉這條邊,兩個集合分別歸 a,b 管 再結合題目,我們就得到了乙個暴力的 n 2 做法 列舉個每條邊,分別對兩棵樹求帶權重心,更新答案 但這顯然是過不了這道題的,考慮對求帶權重心的過程進行優化 設 d x 為 x 所在集合內所有點到他的距離之和,...
BZOJ3302 Shoi2005 樹的雙中心
n 50000的樹,深度 100,有點權,選兩個點x,y,使 最小。dis取了min之後,整個樹就會以某條邊為分界線分成兩半,一半歸乙個點管。如果是兩棵完全獨立的樹的話,那肯定分別取這兩棵樹的帶權重心。但割掉某條邊再找兩邊重心,這種情況不一定是合法情況。例如 上圖中,虛線邊被斷開,兩邊的重心分別是星...