Abandoned country
Time Limit : 8000/4000ms (Java/Other) Memory Limit : 65536/65536K (Java/Other)
Total Submission(s) : 95 Accepted Submission(s) : 26
Font: Times New Roman | Verdana | Georgia
Font Size: ← →
Problem Description
An abandoned country has n(n≤100000) villages which are numbered from 1 to n. Since abandoned for a long time, the roads need to be re-built. There are m(m≤1000000) roads to be re-built, the length of each road is wi(wi≤1000000). Guaranteed that any two wi are different. The roads made all the villages connected directly or indirectly before destroyed. Every road will cost the same value of its length to rebuild. The king wants to use the minimum cost to make all the villages connected with each other directly or indirectly. After the roads are re-built, the king asks a men as messenger. The king will select any two different points as starting point or the destination with the same probability. Now the king asks you to tell him the minimum cost and the minimum expectations length the messenger will walk.
Input
The first line contains an integer T(T≤10) which indicates the number of test cases.
For each test case, the first line contains two integers n,m indicate the number of villages and the number of roads to be re-built. Next m lines, each line have three number i,j,wi, the length of a road connecting the village i and the village j is wi.
Output
output the minimum cost and minimum Expectations with two decimal places. They separated by a space.
Sample Input
1
4 6
1 2 1
2 3 2
3 4 3
4 1 4
1 3 5
2 4 6
Sample Output
6 3.33
Author
HIT
Source
2016 Multi-University Training Contest 1
题目大意是有n个村庄要连通,现在给你m条可以修的路和它们的费用,让求最小的花费,和任取两点为最短路程的期望。
第一问 可以直接上最小生成树,这里可以用prim或者kruskal,prim的邻接表时间是nlogv。kruskal的是nlogn。所以如果选用kruskal的话并查集要进行优化,表示在这里TLE了好多次。
这里采用深度优化,将深度浅的并入深的里面
int find(int son) //这个是原始的找爸爸函数最开始要进行初始化father[i]=i;
{
return father[son]==son?son:find(father[son]);
}
void unite(int x,int y)
{
if(x==y)
return;
else if(rank1[x]<rank1[y])
{
father[x]=y; //如果深度浅,x的爸爸就是y直接连上去
}
else
{
father[y]=x;
if(rank1[x]==rank1[y])
rank1[x]++; //当深度一样时选一个加深,相当于直接连在根的下面
}
}
第二个问题计算期望。
因为条路的价钱都不一样所以概率很容易得出是1/n*(n-1);
然后再求出每条路被经过多少次就可以了
我采用的方法是记录路两边的点各自的子节点有多少个然后相乘就是被经过的次数。
计算子结点可以用dp的想法
int countc(int a,int b)
{
int u[a]=1;//初始点也算经过的一个点
for(i=0;i<v[i].size;i++)
{
if(v[a][i]!=b)
u[a]+=countc(v[a][i],a);
}
return u[a];
}
上面的代码 v[a][i]代表着a能到达的第i个点的值是多少当然这个值不能等于这条线的另一边,因为搜索的是自己的子节点。写过二叉树的话应该容易理解些。
然后直接上代码吧
#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
#define M 100005
using namespace std;
int n,m;
int father[M];
int rank1[M];
struct tree
{
int from,to,cose;
friend bool operator < (tree a,tree b)
{
return a.cose>b.cose;
}
};
priority_queue<tree>q;
vector<int>v[M],w[M];
void getmap()
{
while(!q.empty())
q.pop();
int i,j;
for(i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
tree e;
e.from=a;
e.to=b;
e.cose=c;
q.push(e);
}
}
int find(int son)
{
return father[son]==son?son:find(father[son]);
}
void unite(int x,int y)
{
if(x==y)
return;
else if(rank1[x]<rank1[y])
{
father[x]=y;
}
else
{
father[y]=x;
if(rank1[x]==rank1[y])
rank1[x]++;
}
}
long long ans1;
void kruskal()
{
int i;
for(i=0;i<=n;i++)
{
father[i]=i;
rank1[i]=0;
}
while(!q.empty())
{
tree p=q.top();
q.pop();
int xx,yy;
xx=find(p.from);
yy=find(p.to);
unite(xx,yy);
if(xx!=yy)
{
ans1+=p.cose;
v[p.from].push_back(p.to);w[p.from].push_back(p.cose);
v[p.to].push_back(p.from);w[p.to].push_back(p.cose);
}
}
}
int u[M];
int countc(int a,int b)
{
u[a]=1;
int i;
for(i=0;i<v[a].size();i++)
{
if(v[a][i]!=b)
{
u[a]+=countc(v[a][i],a);
}
}
return u[a];
}
long long ss;
void dfs(int a,int fa)
{
for(int i=0;i<v[a].size();i++)
if(v[a][i]!=fa)
{
int ch=v[a][i];
ss+=(long long)(n-u[ch])*u[ch]*w[a][i];
dfs(ch,a);
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
ans1=0;
ss=0;
getmap();
kruskal();
countc(1,0);
dfs(1,0);
int i;
for(int i=1;i<M;i++)
{
v[i].clear();
w[i].clear();
}
printf("%lld %.2lf\n",ans1,(ss*2.0)/(1.0*n*(n-1)));
}
}