#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxx=1000001;// 最小生成树的这个东西一定不要开小了...
int tot=1,x[maxx],y[maxx];
double ans=0;//注意double
struct data
{
int x,y;
double dis;
}a[maxx];//插歪坐标和路径长
int f[maxx];
int getf(int x)//找爸爸(
{
if(f[x]==x) return x;
else f[x]=getf(f[x]);
return f[x];
}
double dit(int x1,int x2,int y1,int y2)//注意double
{
return sqrt(double(pow(x1-x2,2))+double(pow(y1-y2,2)));
}
bool cmp(data a,data b)//比较路径长
{
return a.dis<b.dis;
}
void merge(int x, int y) //合并
{
x=getf(x);
y=getf(y);
if(x!=y) f[y]=x;
}
int main()
{
int n,m,i,j;
cin>>n>>m;
for(i=1;i<=n;i++)//茶几初始化
{
f[i]=i;
}
for(i=1;i<=n;i++)//普通输入
{
cin>>x[i]>>y[i];
}
for(i=1;i<=n;i++)
{
for(j=i+1;j<=n;j++)
{
a[tot].x=i;
a[tot].y=j;
a[tot].dis=dit(x[i],x[j],y[i],y[j]);
tot++;
}
}
for(i=1;i<=m;i++)//已联通输入
{
int t,tt;
cin>>t>>tt;
a[++tot].x=t;
a[tot].y=tt;
a[tot].dis=0;
}
sort(a+1,a+1+tot,cmp);//排序
int k=0;
for(i=1;i<=tot;i++) //kruskal
{
if(getf(a[++k].x)!=getf(a[k].y))
{
ans+=a[k].dis;
merge(a[k].x,a[k].y);
}
}
printf("%.2lf",ans);//保留两位
return 0;
}
RE真好玩