Abandoned country

HDU5723Abandoned country

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

题目大意:

①有n个点,m条边,需要建立一个图,使得花费最小并且每个点之间保证有路通达而且没有环,求最小花费。

②建完路之后,随机选出两个点,求其最小期望值。

PS:其中需要注意的就是“随机选出两个点,求其最小期望值。”怎么求,枚举图中的任意一对顶点的路径长度然后乘以该边的概率再全部加起来吗。
还不理解吗看这里https://blog.csdn.net/mengxiang000000/article/details/51957529

问题就在于顶点有那么多105,肯定超时了,我们可以换一种思路,边有多少条?n-1条!那么我们可以把上面的某对顶点之间的路径长度乘以该边的概率转换为枚举每一条边经过的次数乘以它的权值再除于总的选取数C(2,n-1)。

解题步骤:
1.Kruskal求最小生成树,并把最小生成树作为无向图存储起来
2.对无向图(因为你从1或者任意点开始dfs)进行dfs。

AC代码如下:(有详细注释)
代码中的并查集的ll全部可以改为int的当时错了几次就病急乱投医了改为ll了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long int
struct node{
 ll u;
 ll v;
 ll w;
}q[1000005];//m≤1000000 
ll f[100005];
int book[100005]; 
ll n,m;//顶点数,边数 
vector<pair<ll ,ll> > p[100005];//动态数组记录图 (生成树的图),为了后面dfs出经过每条边的次数
ll ans=0ll;
bool operator < (struct node A,struct node B)
{
    return A.w<B.w;
}
ll getf(ll u)
{
    if(f[u]==u)
        return u;
    f[u]=getf(f[u]);//路径压缩
    return f[u];
}
ll merge(ll u,ll v)
{
    ll t1=getf(u);
    ll t2=getf(v);
    if(t1!=t2)
    {
        f[t2]=t1;//靠左原则
        return 1;
    }
    return 0;
}
/*假如是某条边为 u->v =w 
 走过该边的次数=左子树(以u为根的树的节点,包括u)的节点数 * 右子树的点(以v为根的树的节点,包括v)的节点数 
		       =  (n-right)							 *  right 
 //其中“左子树(以u为根的树的节点,包括u)的节点数 ”可以直接通过  n-“右子树的点(以v为根的树的节点,包括v)的节点数” 求得 
*/
/*示意图: 
	\			/
	—u --w-- v — 
	/			\ 
				4
		      4 4 
				4 
*///=4*8-4=16
ll dfs(ll u)//搜索出 每一条边 的贡献 
{
	ll right=0ll,all=0ll;
	for(ll i=0;i<p[u].size() ;i++){
		ll v=p[u][i].first;//顶点编号
		if(book[v]==0)
		{
			book[v]=1;//标记
			right=dfs(v);//就是以b为根的树的节点数包括b
			all+=right;//记录以a为根的右子树节点数 
			ans+= (n-right)/*与x相连的左子树节点数*/*right/*与b相连的右子树的节点数*/*p[u][i].second;//该边经过的次数*该边的权值 
		}//层层递归知道只有本身的子树(1),逐步返回并计算出每一条表的贡献 
	}
	return all+1;//此节点经过搜索后所有的与此节点连接的节点数 返回的时候要把自己(根)计算进去 
}//可以理解为从右向左返回节点数 
//数学期望值=各个(x*频率)之和
int main(void)
{
    
    int t;
	ll sum;
    scanf("%d",&t);
    while(t--)
    {
    	ans=0ll;
        sum=0ll;
        scanf("%lld %lld",&n,&m);
    	for(ll i=0;i<=n;i++){//注意下标是1到n的,由于一直遍历0到n-1错了好多次 
            f[i]=i;
            p[i].clear() ;
            book[i]=0;
        }
        //初始化 
        for(ll i=0;i<m;i++){
            scanf("%lld %lld %lld",&q[i].u,&q[i].v,&q[i].w);
        }
        if(m==0||n==0)
        {
			printf("0 0.00\n");
			continue;
		}
        sort(q,q+m);
        int count=0;
        for(ll i=0;i<m;i++){
            if(merge(q[i].u,q[i].v))
            {
                sum+=q[i].w;
                count++;
                //储存为有向图
				p[q[i].u ].push_back(make_pair(q[i].v ,q[i].w ));
				p[q[i].v ].push_back(make_pair(q[i].u ,q[i].w ));
            }
            if(count>=n-1)
                break;
        }
        //生成树被转换为无向图了,进行dfs
        book[1]=1;//从1号顶点开始 
		dfs(1);
		//printf("ans:%lld\n",ans);
        double num=1.0*n*(n*1.0-1.0)/2.0;//总的路径数 
        printf("%lld %.2f\n",sum,double(ans)/num);
    }
    return 0;
}
发布了68 篇原创文章 · 获赞 15 · 访问量 9016

猜你喜欢

转载自blog.csdn.net/qq_43791377/article/details/103047648