習題:Castle(貪心)
阿新 • • 發佈:2020-07-31
題目
思路
我們考慮交換兩個子樹的訪問順序會導致什麼
設\(w[i]\)為以i為根節點的子樹內部所有的邊權和加上i到i的父親的邊權
設\(siz[i]\)為以i為根節點的子樹內部有多少個點
如果i在j前面,所提供的的多的貢獻為
\(w[i]*siz[j]\)
如果j在i前面,所提供的的多的貢獻為
\(w[j]*siz[i]\)
如果i在j前面更優,則一定有
\(w[i]*siz[j]<w[j]*siz[i]\\\Rightarrow \frac{w[i]}{siz[i]}<\frac{w[j]}{siz[j]}\)
基於此,我們對每一個節點進行排序,再用一次dfs統計答案即可
程式碼
#include<iostream> #include<algorithm> #include<vector> using namespace std; struct node { int e; long long w; }; int n; int siz[100005]; long long dp[100005],ans; vector<node> g[100005]; bool cmp(const node &a,const node &b) { double t1=1.0*dp[a.e]/siz[a.e]; double t2=1.0*dp[b.e]/siz[b.e]; return t1<t2; } void dfs(int u,int fa) { siz[u]=1; for(int i=0;i<g[u].size();i++) { int v=g[u][i].e; if(v!=fa) { dfs(v,u); siz[u]+=siz[v]; dp[v]+=g[u][i].w; dp[u]+=dp[v]; } } sort(g[u].begin(),g[u].end(),cmp); } void calc(int u,int fa,long long &ti) { ans+=ti; //cout<<u<<' '; for(int i=0;i<g[u].size();i++) { int v=g[u][i].e; if(v!=fa) { ti+=g[u][i].w; calc(v,u,ti); ti+=g[u][i].w; } } } int main() { //ios::sync_with_stdio(false); cin>>n; for(int i=1;i<n;i++) { int u,v; long long w; cin>>u>>v>>w; g[u].push_back((node){v,w}); g[v].push_back((node){u,w}); } dfs(1,0); /*for(int i=1;i<=n;i++) { cout<<dp[i]<<' '<<siz[i]<<'\n'; }*/ long long ti=0; calc(1,0,ti); printf("%.8lf",1.0*ans/(n-1)); return 0; }