标签:nal 题解 博客 space c++ names freopen shu include
把n+m个点分为两列,第一列有n个点,第二列有m个点。
每一列的点的横坐标相同,给这些点连边,将这n+m个点全部连通,求所连边的最小总和。
第一行四个整数n,m,x1,x2,分别表示第一列的点数,第二列的点数,第一列中点的横坐标,第二列中的点的横坐标。
第二行n个正整数y1[1],y1[2],...,y1[n],表示第一列中的点的纵坐标。
其中,第一个数表示第一个点的纵坐标,接下来的数表示当前点与前一个点的纵坐标差。
第三行有m个正整数,意义同上。
一个实数,为边的最小总和,答案保留两位小数。
n,m<=600000,所有点的纵坐标不超过10^8,x1<x2<2000.
2 3 1 3
1 2
2 2 1
7.24
这道题自认为是今天的签到题(蒟蒻只会写签到题的题解了)。
可以证明一个点最多连四条边。
即与同列的前一个点和后一个点各连一条。
然后以该点的纵坐标作一条平行于x轴的虚线。
与另一列的虚线上第一个点和虚线下第一个点各连一条边。
可以证明这两个点之一肯定是另一列离这个点最近的点。
然后跑kruscal就行了。
(其实正解是DP,但既然kruscal卡常卡过了就不写DP了吧2333 (bmw))
#include<bits/stdc++.h> #define inf 1030032399 using namespace std; const int N=6e5+5; int n,m,len,xI,xII,yI[N],yII[N],pa[N*2]; double ans,minn=inf; struct node{ int x,y;double z; bool operator < (const node &mt)const { return z<mt.z; } }f[N*4]; inline int read(){ int r=0,t=1,c=getchar(); while(c<‘0‘||c>‘9‘) t=c==‘-‘?-1:1,c=getchar(); while(c>=‘0‘&&c<=‘9‘) r=r*10+c-48,c=getchar(); return r*t; } double dis(int x,int y){ double sss=x,llf=y; return sqrt((double)(xII-xI)*(double)(xII-xI)+(llf-sss)*(llf-sss)); } int Find(int now){ if(now==pa[now]) return now; return pa[now]=Find(pa[now]); } void kruscal(){ int cnt=0; for(int i=1;i<=n+m;i++) pa[i]=i; for(int i=1;i<=len;i++){ int zjy=Find(f[i].x),tlj=Find(f[i].y); if(zjy!=tlj){ pa[zjy]=tlj; cnt++;ans+=f[i].z; if(cnt==n+m-1) return ; } } } int main(){ freopen("canal.in","r",stdin); freopen("canal.out","w",stdout); int x; n=read(),m=read(),xI=read(),xII=read(); yI[1]=read(); for(int i=2;i<=n;i++){ x=read(); yI[i]=yI[i-1]+x; f[++len]=(node){i-1,i,x*1.}; } yII[1]=read(); for(int i=2;i<=m;i++){ x=read(); yII[i]=yII[i-1]+x; f[++len]=(node){i-1+n,i+n,x*1.}; } int now=0; for(int i=1;i<=n;i++){ for(int j=now+1;j<=m;j++){ if(yII[j]>=yI[i]) break; now=j; } if(now) f[++len]=(node){i,now+n,dis(yI[i],yII[now])}; f[++len]=(node){i,now+n+1,dis(yI[i],yII[now+1])}; } sort(f+1,f+len+1); kruscal(); printf("%.2f",ans); return 0; }
标签:nal 题解 博客 space c++ names freopen shu include
原文地址:https://www.cnblogs.com/Biscuits0819/p/11336354.html