习题: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;
}
posted @ 2020-07-31 12:28  loney_s  阅读(125)  评论(0)    收藏  举报