codevs 1419 藤原妹红

codevs 1419 藤原妹红

题目描述 Description

在幻想乡,藤原妹红是拥有不老不死能力的人类。虽然不喜欢与人们交流,妹红仍然保护着误入迷途竹林村民。由于算得上是幻想乡最强的人类,对于她而言,迷途竹林的单向道路亦可以逆行。在妹红眼中,迷途竹林可以视为一个由N个路口(编号1..N),M条不同长度双向路连接的区域。妹红所在的红之自警队为了方便在迷途竹林中行动,绘制了一张特殊的迷途竹林地图,这张地图上只保留了N-1条道路,这些道路保证了任意两个路口间有且仅有一条路径,并且满足所有保留的道路长度之和最小,我们称这些道路为『自警队道路』。现在妹红打算在其中一个连接有多条『自警队道路』的路口设立根据地,当去掉根据地这个根据地所在路口后,就会出现某些路口间无法通过『自警队道路』相互连通的情况,我们认为这时仍然能够通过『自警队道路』连通的路口属于同一个『区域』。妹红希望最后每个『区域』的『自警队道路』总长尽可能平均,请计算出她应该选择哪一个路口作为根据地。

(尽可能平均即权重最小,设每一块『区域』的路线总长为Length[i],平均路线长度为Avg=SUM{Length[i]}/区域数,权重d=∑( (Length[i]-Avg)^2 ) )

输入描述 Input Description
第1行:2个正整数N,M
第2..M+1行:每行2个整数u,v和1个实数len,表示u,v之间存在长度为len的边

输出描述 Output Description
第1行:1个整数,最后选择的路口编号,存在多个可选路口时选择编号小的

样例输入 Sample Input
3 3
3 1 5
3 2 4
1 2 3

样例输出 Sample Output
2

数据范围及提示 Data Size & Hint
对于60%的数据:3 ≤ N ≤ 2,000,N-1 ≤ M ≤ 50,000
对于100%的数据:3 ≤ N ≤ 40,000,N-1 ≤ M ≤ 200,000
对于100%的数据:0 < len ≤ 100,000,000

提示
  样例解释:
  妹红的『自警队道路』为(1,2)和(2,3)。
  只能选择2作为根据地,产生的两个区域Length[i]分别为3和4。
  所以方差为:(4-3.5)^2 + (3-3.5)^2 = 0.5

  注意:
  保证不存在相同距离的线路,两个路口间可能出现多条路径,且任意点对间至少存在一条路径。

思路:留下n-1条边来联通而且要路径之和最小,很容易想到要建最小生成树。
注意精度问题。

题解:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=400000+10;
struct re{//记录路径 
    int x,y;
    double z;
}a[maxn];

int cmp(re a,re b)
{
    return a.z<b.z;
}

struct cc{//保留的路径 
    int from,to;
    double cost;
}es[maxn];

int first[maxn],next[maxn];
int tot=0;
void build(int ff,int tt,double pp)
{
    es[++tot]=(cc){ff,tt,pp};
    next[tot]=first[ff];
    first[ff]=tot;
}
int f[maxn];
int find(int w)
{
    if(w!=f[w])
    {
        f[w]=find(f[w]);
    }
    return f[w];
}
double dis[maxn];
double maxe=1e30;//一定要开大 
int num[maxn];//记录连的边数
int ans;
double sum=0;
int fa[maxn];
double dfs(int u)
{
    double ave=sum/(double)num[u];
    double d=0,size=0;
    for(int i=first[u];i;i=next[i])
    {
        int v=es[i].to;
        if(v!=fa[u])
        {
            fa[v]=u;
            dis[v]=es[i].cost+dfs(v);
            d+=(dis[v]-ave)*(dis[v]-ave);
            size+=dis[v];
        }
    }
    d+=(sum-size-ave)*(sum-size-ave);
    if(num[u]!=1)
    {
        if(d<maxe) maxe=d,ans=u;
        else if(d==maxe) ans=min(ans,u);
    }
    return size;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        double len;
        scanf("%d%d%lf",&u,&v,&len);
        a[i].x=u,a[i].y=v,a[i].z=len;
    }
    sort(a+1,a+m+1,cmp);
    for(int i=1;i<=n;i++)
    {
        f[i]=i;
    }
    for(int i=1;i<=m;i++)//建最小生成树 
    {
        if(find(a[i].x)!=find(a[i].y))
        {
            f[find(a[i].x)]=find(a[i].y);
            build(a[i].x,a[i].y,a[i].z);
            build(a[i].y,a[i].x,a[i].z);
            num[a[i].x]++,num[a[i].y]++;
            sum+=a[i].z;
        }
    }
    dfs(1);
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值