NOIp模拟 电缆建设

来源:互联网 发布:zz的网络意思 编辑:程序博客网 时间:2024/06/02 15:47
【问题描述】
教主上电视了,但是蔚蓝城郊区沿河的村庄却因电缆线路老化而在直播的时
候停电,这让市长SP先生相当的愤怒,他决定重修所有电缆,并改日播放录像,
杜绝此类情况再次发生。
河流两旁各有n,m个村庄,每个村庄可以用二维坐标表示,其中河流一旁的
村庄横坐标均为x1,河流另一旁的村庄横坐标均为x2。由于地势十分开阔,任意
两个村庄可以沿坐标系直线修建一条电缆连接,长度即为两村庄的距离。要修建
若干条电缆,使得任意两个村庄都可以通过若干个有电缆连接的村庄相连。
因为修建的经费与长度成正比,SP市长当然希望所花的钱越少越好,所以他
希望你来帮助他设计一套方案,使得电缆总长度最小,并告诉所需要的电缆总长
度。
【输入格式】
输入的第1行为四个正整数,n,m,x1,x2,表示河流两旁的村庄数以及横坐
标。
第2行有n个正整数y1[1], y1[2]... y1[n],描述了横坐标为x1的村庄的纵坐标。第1
个整数为纵坐标最小的那个村庄的纵坐标,从第2个整数开始,第i个整数代表当
前村庄与前一个村庄的纵坐标差,即y[i]-y[i-1]。
第3行有m个正整数y2[1], y2[2]... y2[n],用同样的方法描述了横坐标为x2的村庄
的纵坐标。
【输出格式】
输出仅包括一个实数,为最小的总长度,答案保留两位小数。
【样例输入】
2 3 1 3
1 2
2 2 1
【样例输出】
7.24
【样例解释】
按如下方案建设电缆,括号内代表村庄的坐标,“-”代表有电缆连接。
(1,1)-(1,3)
(1,3)-(3,4)
(3,4)-(3,2)
(3,4)-(3,5)
【数据规模】
对于20%的数据,n,m≤10;
对于40%的数据,n,m≤1000;
对于70%的数据,n,m≤100000;
对于100%的数据,n,m≤600000,所有村庄纵坐标不超过10^8,x1<x2<2000,

输入文件不超过4M。

难点:建图。

一看就是mst,但是这个数据比较大。

不能无脑加边。

#include<cmath>#include<queue>#include<cstdio>#include<cstring>#include<cstdlib>#include<iostream>#include<algorithm>#define N 666666#define LL long longusing namespace std;LL n,m,x1,x2,a[N],b[N],f[N*2];LL cnt=0;double ans=0;struct Edge{    int u,v;    double dist;}e[N*10];int findf(int x){    if (f[x]!=x) f[x]=findf(f[x]);    return f[x];}bool cmp(Edge a,Edge b){    return a.dist <b.dist;}double calc(int k1,int k2){    return sqrt((double)(x1-x2)*(x1-x2)+(a[k1]-b[k2])*(a[k1]-b[k2]));}void init(){    scanf("%d%d%d%d",&n,&m,&x1,&x2);    for (int i=1;i<=n;i++)    {        scanf("%d",&a[i]);        a[i]+=a[i-1];        f[i]=i;    }    for (int i=1;i<=m;i++)    {        scanf("%d",&b[i]);        b[i]+=b[i-1];        f[i+n]=i+n;    }    int id=1;    for(int i=1;i<=n;i++)    {        if (i!=n) e[++cnt]=(Edge) {i,i+1,a[i+1]-a[i]};        while (id<m && calc(i,id) > calc(i,id+1)) id++;//找河对面与i点最近的        e[++cnt]=(Edge) {i,id+n,calc(i,id)};        if (id>1) e[++cnt]=(Edge) {i,id-1+n,calc(i,id-1)};        if (id<m) e[++cnt]=(Edge) {i,id+1+n,calc(i,id+1)};        /*离i点最近的及其相邻的b村庄的点加入边集数组*/    }    for (int i=1;i<=m;i++)        if (i!=m) e[++cnt]=(Edge) {i+n,i+n+1,b[i+1]-b[i]};}void kruskal(){    sort(e+1,e+cnt+1,cmp);    int rst=n+m;    for (int i=1;i<=cnt && rst>1;i++)    {        int x=findf(e[i].u);        int y=findf(e[i].v);        if (x!=y)        {            f[x]=f[y];            rst--;            ans+=e[i].dist;        }    }    printf("%.2lf",ans);}int main(){    //freopen("cable.in","r",stdin);//freopen("cable.out","w",stdout);    init();    kruskal();    return 0;}


0 0