ACM2016多校联赛 Abandoned country 最小生成树

又查错了一下午。。666.

题目 :http://acm.hdu.edu.cn/showproblem.php?pid=5723


工具 : 1.并查集(压缩路径)2.优先队列 3.向量(Vector).

解决这个问题分两步:

1.简单的最小生成树,计算路径.。直接用kruskal()可以解决;

2.建树之后(前提很重要),求出 任意两点的距离的总和,然后除以所有可能组合方式:n*(n-1)/2。A

求任意两点距离(难点):任一条路径,只需计算它被走过了次数,就可以求出所有的距离。

而走过的次数:只需要知道一条路径的两个端点A,B,以一个端点A为父节点,计算不经过另一个端点B的所有子节点的个数(包括自己),得到点的个数为NA。

再通过节点总数N,由 NB=N-NA;得到B端的节点数目。所以走过次数为(NA*NB);具体算法见题解。MST();


此外就是一些细节处理,主要是记得每条路径可以为 1000000,因此需要开 long long.

#include<cstdio>
#include<queue>
#include<vector>
#include<cstring> 
using namespace std;
struct node
{
    int from,to,cost;
    friend bool operator <(const node&a,const node&b)
    {
        return a.cost>b.cost;
    } 
}line[100005],l;

struct mst
{
    int to,cost;
    mst(int t,int c)
    :to(t),cost(c){}
};

int pre[100005],sum[1000005],n,m;
long long dist[100005];
priority_queue<node>q;
vector<mst>vec[100005];

int find(int x)     //并查集
{
    int r=x;
    while(r!=pre[r])
    r=pre[r];
    int i=x,j;
    while(r!=pre[i])
    {
        j=pre[i];
        pre[i]=r;
        i=j;
    }
    return r;
}

long long kruskal()          //得最小生成树权值
{
    long long ans=0;
    for(int i=1;i<=n;++i)
    pre[i]=i,vec[i].clear();
    int tmp=0 ;
    
    while(!q.empty()&&tmp!=n-1)
    {
        l=q.top();
        q.pop();
        
        int ff=find(l.from);
        int ft=find(l.to);
        
        if(ff!=ft)
        {
        	tmp++;
            pre[ft]=ff;
            ans+=l.cost;
            
            vec[l.from].push_back(mst(l.to,l.cost));
            vec[l.to].push_back(mst(l.from,l.cost));
        }
    }
    return ans;
}

void dfs(int From,int To)   //核心 : 计算每条边走过次数,并计算出距离;
{
    sum[From]=1;
    for(int i=0;i<(int)vec[From].size();++i)
    {
        if(vec[From][i].to==To)continue;
        dfs(vec[From][i].to,From);
        sum[From]+=sum[vec[From][i].to];
        dist[From]+=dist[vec[From][i].to]+((long long)sum[vec[From][i].to]*(n-sum[vec[From][i].to])*vec[From][i].cost);
    }
}
double MST() 
{
    double ans=0;
    memset(dist,0,sizeof(dist));
    dfs(1,-1);
    long long s=(long long)n*(n-1)/2;
    return dist[1]*1.0/s;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        while(!q.empty())
        q.pop();
        
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;++i)
        {
            scanf("%d%d%d",&l.from,&l.to,&l.cost);
            q.push(l);                             //获得路径并排序;
        }
        
        printf("%lld ",kruskal());
        printf("%.2lf\n",MST());
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值