1. 程式人生 > 實用技巧 >習題:Castle(貪心)

習題: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;
}