BSOJ 2923:藤原妹红 MST+树型DP

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


 
(尽可能平均即权值最小,设每一块【区域】的路线总长为Length[i](包括连接【根据地】与该【区域】的边),平均路线长度为Avg=SUM{Length[i]}/区域数,权值d=∑ (Length[i]-Avg)^2

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

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

Sample Input
3 3
3 1 5
3 2 4
1 2 3

Sample Output
2

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

【数据范围】
对于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

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

我妈这道题真的是太裸了...
我tm考试的时候竟然没写出来
注意到这里对于length的定义:设每一块【区域】的路线总长为Length[i](包括连接【根据地】与该【区域】的边)。
那么问题就变得简单了起来:对于一个断点,它可以断出等同其度数的独立区域,对于它的子树,这个区域的边权和用树型DP简单解决,对于其上的树,其权和=tot_value-tot_son_val,对于avg,其等于tot_value/du[x]。
这样下枚举每个点就完了...
因为有点定义的混乱,没敢多去想树D,注意一下,一个点只有一个父节点啊!!!!

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define inf 1e70
using namespace std;
struct node
{
    int x,y;
    double val;
}a[1000005];
struct ode
{
    int to,next;
    double val;
}w[1000005];
int h[100005]={0},cnt=0,_cnt=0,fa[100005]={0},p[100005]={0};
int du[100005]={0};
double v[1000006];
int n,m;
double tot=0.0;
int getfa(int x)
{
    if(x==fa[x])return x;
    return fa[x]=getfa(fa[x]);
}
bool cmp(node q,node w)
{
    return q.val<w.val;
}
void init()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        int j,k;double l;
        scanf("%d%d%lf",&j,&k,&l);
        a[++_cnt].x=j;
        a[_cnt].y=k;
        a[_cnt].val=l;
    }
    sort(a+1,a+_cnt+1,cmp);
}
void Addarc(int x,int y,double val)
{
    cnt++;w[cnt].to=y;w[cnt].next=h[x];h[x]=cnt;
    w[cnt].val=val;
}
void K()
{
    int e=0;
    for(int i=1;i<=_cnt;i++)
    {
        int f1=getfa(a[i].x);
        int f2=getfa(a[i].y);
        if(f1!=f2)
        {
            fa[f1]=f2;
            e++;
            tot+=a[i].val;
            Addarc(a[i].x,a[i].y,a[i].val);
            Addarc(a[i].y,a[i].x,a[i].val);
            du[a[i].x]++;
            du[a[i].y]++;
            if(e==n-1)break;
        }
    }
}
void work()
{
    int k;
    double minn=1e80;
    for(int x=1;x<=n;x++)
    {
        if(du[x]<=1)continue;
        double avg=tot/du[x];
        double temp=0.0,s=0.0;
        for(int i=h[x];i;i=w[i].next)
        {
           int to=w[i].to;
           if(to==p[x])continue;
           s+=v[to];
           temp+=(w[i].val+v[to]-avg)*(w[i].val+v[to]-avg);
        }
        if(x!=1)temp+=(tot-v[x]-avg)*(tot-v[x]-avg);
        if(minn>temp)
        {
            minn=temp;
            k=x;
        }
    }
    printf("%d\n",k);
}
void DP(int x,int fa)
{
    for(int i=h[x];i;i=w[i].next)
    {
        int to=w[i].to;
        if(to==fa)continue;
        p[to]=x;
        DP(to,x);
        v[x]+=v[to]+w[i].val;
    }
}
int main()
{
    init();
    K();
    DP(1,0);
    work();
return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值