- 题目链接 : http://acm.hdu.edu.cn/showproblem.php?pid=5723
- 题意 :有n个村庄,m条道路,每条道路有一个权值,现求最小的权值和使得所有村庄可以连通。并且求任意两点间距离的期望值。
- 思路 :第一问,最小权值和,毫无疑问是最小生成树。这里我用的是Kruskal算法。
对最小生成树不理解的同学可以去做hdu1863入门,这里是题解 https://blog.csdn.net/qq_39763472/article/details/82924175
第二问,是一道树形dp题,对树形dp不理解的同学可以去做hdu2376入门。这里是题解 https://blog.csdn.net/qq_39763472/article/details/82894446
总的来说,这道题相当于是将上述两道题合并起来,在Kruskal算法求最小生成树时,把它保存起来,然后dfs即可。 - 代码 :
#include "bits/stdc++.h"
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define fori(i,l,u) for(int i = l;i < u;i++)
#define forj(j,l,u) for(int j = l;j < u;j++)
#define F first
#define S second
#define pb push_back
#define mk make_pair
typedef long long ll;
typedef pair<int, int> pi;
typedef pair<string, int> ps;
typedef vector<int> vi;
typedef vector<string> vs;
typedef vector<pi> vpi;
const int maxn = 1e5 + 6;
int t;
ll n,m; //n是村庄,m是道路
ll ans; //最小生成树的权值
ll node[maxn],rk[maxn];
ll sum[maxn],dp[maxn];
typedef struct {
ll u;
ll v;
ll w;
}road;
road R[maxn * 10];
typedef struct {
ll v;
ll w;
}N;
vector<N> tree[maxn];
void init(){ //初始化
ans = 0;
mem(sum,0);
mem(dp,0);
fori(i, 1, n+1){
node[i] = i;
rk[i] = 0;
tree[i].clear();
}
}
ll find(ll x){
if (x == node[x]) {
return x;
}
return node[x] = find(node[x]);
}
void merge(ll x,ll y){
x = find(x);
y = find(y);
if (rk[x] < rk[y]) {
node[x] = y;
}
else{
node[y] = x;
if (rk[x] == rk[y]) {
rk[x] ++;
}
}
}
bool compare(const road& x,const road &y){
return x.w < y.w;
}
void Kruskal(ll m){
int num = 0;
for(int i = 0;i < m && num != n-1 ;i++){
if (find(R[i].u) != find(R[i].v)) {
num ++;
ans += R[i].w;
merge(R[i].u,R[i].v);
// 添加最小生成树
N t1,t2;
t1.v = R[i].u; t1.w = R[i].w;
t2.v = R[i].v; t2.w = R[i].w;
tree[R[i].v].pb(t1);
tree[R[i].u].pb(t2);
}
}
}
void dfs(ll cur,ll ft){
sum[cur] = 1;
fori(i, 0, tree[cur].size()){
ll son = tree[cur][i].v;
ll len = tree[cur][i].w;
if (ft == son) { //至关重要
continue;
}
dfs(son, cur);
sum[cur] += sum[son];
dp[cur] += dp[son] + (n-sum[son])*sum[son]*len;
}
}
int main()
{
// freopen("1.txt", "r", stdin);
scanf("%d",&t);
while (t--) {
scanf("%lld%lld",&n,&m);
init();
fori(i, 0, m){ //道路的标号从0开始
scanf("%lld%lld%lld",&R[i].u,&R[i].v,&R[i].w);
}
sort(R, R+m,compare);
Kruskal(m);
ll cur = 0;
fori(i, 1, n+1){
if (node[i] == i) {
cur = i;
break;
}
}
dfs(cur, -1);
double num = n*(n-1)/2;
cout<<ans<<" ";
printf("%.2lf\n",(double)dp[cur]/num);
}
return 0;
}
此代码和上述两题的结构一样,基本上是抄板子的。
- 遇到的问题 : 说的轻松,遇到的问题真是。。心酸
1)注意n和m的范围,一个是10w,一个是100w,设置maxn = 1e5 + 6,然后边的结构体数组的范围是 10 * maxn。
2)int会wa。。虽然不知道为什么,有一道最小生成树还是树形dp的题也是这样,不过都改成ll肯定没错。。
3)cin会wa,这两种类型的题,最小生成树,树形dp,一般都用scanf,有的题会有hint : scanf recommend
4)dfs和Kruskal注意边界问题。