习题:Castle(贪心)
题目
思路
我们考虑交换两个子树的访问顺序会导致什么
设\(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;
}

浙公网安备 33010602011771号