题目: http://codeforces.com/problemset/problem/101/D
题意:
给定一颗树,和每次经过一条边的时间花费;
初始人物在点1处,他将走过所有的点;
每条边至多经过两次;
当走到点i时,ans+=当前时间;
最后输出最小的ans/(树上总点数-1)。
分析:
关键在于一个点有多颗子树时,如何选择走行顺序。
考虑点root有两颗子树s1和s2;
设f[s1]是先走s1子树再走s2子树时子树s1对答案的贡献,
f[s2]是先走s2子树再走s1子树时子树s2对答案的贡献,
d[s1]是从root开始走过所有s1节点并回到root的时间花费,
d[s2]是从root开始走过所有s2节点并回到root的时间花费,
num[s1]和num[s2]分别是子树s1和s2所含节点个数;
若s1比s2先经过,则满足:
f[s1]+d[s1]*num[s2]+f[s2]<f[s2]+d[s2]*num[s1]+f[s1]
即d[s1]/num[s1]<d[s2]/num[s2]。
以次为贪心标准统计答案即可。
代码:
#include <bits/stdc++.h>
#define Pii pair<int,int>
using namespace std;
typedef long long llong;
const int tmax=1e5+5;
int n,num[tmax];
vector<Pii> G[tmax],Next[tmax];
llong d[tmax],vis,ans;
struct dPii{
double val;
int index,cost;
bool operator<(const dPii &rhs)const
{
return val>rhs.val;
}
};
void dfs(int x,int fa)
{
int len=G[x].size();
num[x]=1;
priority_queue<dPii> Q;
for(int i=0;i<len;i++)
{
int v=G[x][i].first;
if(v==fa) continue;
dfs(v,x);
num[x]+=num[v];
d[v]+=G[x][i].second*2;
d[x]+=d[v];
Q.push(dPii{1.0*d[v]/num[v],v,G[x][i].second});
}
while(!Q.empty())
{
Next[x].push_back(Pii(Q.top().index,Q.top().cost));
Q.pop();
}
return;
}
void getAns(int x)
{
ans+=vis;
int len=Next[x].size();
for(int i=0;i<len;i++)
{
vis+=Next[x][i].second;
getAns(Next[x][i].first);
vis+=Next[x][i].second;
}
return;
}
int main()
{
int u,v,c;
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&u,&v,&c);
G[u].push_back(Pii(v,c));
G[v].push_back(Pii(u,c));
}
dfs(1,0);
getAns(1);
printf("%.10lf",((double)ans)/(num[1]-1));
return 0;
}