#include <bits/stdc++.h>
using namespace std;

using VI = vector<int>;
using VVI = vector<VI>;
using PII = pair<int, int>;
using LL = long long;
using VL = vector<LL>;
using VVL = vector<VL>;
using PLL = pair<LL, LL>;
using VS = vector<string>;

#define ALL(a)  begin((a)),end((a))
#define RALL(a) (a).rbegin(), (a).rend()
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define SZ(a) int((a).size())
#define SORT(c) sort(ALL((c)))
#define RSORT(c) sort(RALL((c)))
#define UNIQ(c) (c).erase(unique(ALL((c))), end((c)))

#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define REP(i,n)  FOR(i,0,n)

#define FF first
#define SS second

#define DUMP(x) cout<<#x<<":"<<(x)<<endl
template<class S, class T>
istream& operator>>(istream& is, pair<S,T>& p){
  return is >> p.FF >> p.SS;
}
template<class T>
istream& operator>>(istream& is, vector<T>& xs){
  for(auto& x: xs)
	is >> x;
  return is;
}
template<class S, class T>
ostream& operator<<(ostream& os, const pair<S,T>& p){
  return os << p.FF << " " << p.SS;
}
template<class T>
ostream& operator<<(ostream& os, const vector<T>& xs){
  for(unsigned int i=0;i<xs.size();++i)
	os << (i?" ":"") << xs[i];
  return os;
}
template<class T>
void maxi(T& x, T y){
  if(x < y) x = y;
}
template<class T>
void mini(T& x, T y){
  if(x > y) x = y;
}


const double EPS = 1e-10;
const double PI  = acos(-1.0);
const LL MOD = 1e9+7;

class Point{
public:
  double x, y;

  Point(double x = 0.0, double y = 0.0) : x(x), y(y){}

  Point& operator += (const Point& p){ x += p.x; y += p.y; return *this;}
  Point operator + (const Point& p) const{ Point q = *this;return q += p;}
  Point& operator -= (const Point& p){ x -= p.x; y -= p.y; return *this;}
  Point operator - (const Point& p) const{ Point q = *this; return q -= p;}
  Point& operator *= (double a){ x *= a; y *= a; return *this;}
  Point operator * (double a) const{ Point q = *this; return q *= a;}
  Point& operator /= (double a){ x /= a; y /= a; return *this;}
  Point operator / (double a) const{ Point q = *this; return q /= a;}

  double abs() const { return sqrt(norm()); }
  double norm() const { return x*x + y*y; }
  Point& normalize(){ return *this /= abs(); }
  
  bool operator < (const Point &p) const{
	return x != p.x ? x < p.x : y < p.y;
  }
  
  //EPSは十分小さな値
  bool operator == (const Point &p) const {
	return fabs(x - p.x) < EPS && fabs(y - p.y) < EPS;
  }
  friend ostream& operator <<(ostream& os, const Point& p);
};
ostream& operator <<(ostream& os, const Point& p){
  return os << "(" << p.x << ","<<p.y << ")";
}

class Segment{
public:
  Point p1, p2;
  Segment(){}
  Segment(const Point& p1_, const Point& p2_)
	: p1(p1_), p2(p2_)
  {}
};

class Circle{
public:
	Point c;
	double r;
};

typedef vector<Point> Polygon;

//点を線として扱う
typedef Point Vector;
//線分を直線として扱う
typedef Segment Line;

double dot(Vector a, Vector b){
	return a.x*b.x + a.y*b.y;
}
double cross(Vector a, Vector b){
	return a.x*b.y - a.y*b.x;
}
int ccw(Point p0, Point p1, Point p2){

	//p0->p1をp0始点、p1終点の基準ベクトルとしたとき

	//p0->p2が反時計回りになるとき
	static const int COUNTER_CLOCKWISE = 1;
	//p0->p2が時計回りになるとき
	static const int CLOCKWISE = -1;
	//p2,p0,p1の順で同一直線上にあるとき
	static const int ONLINE_BACK = 2;
	//p0,p1,p2 の順で同一直線上にある場合
	static const int ONLINE_FRONT = -2;
	//p2 が線分 p0p1 上にある場合
	static const int ON_SEGMENT = 0;

	Vector a = p1 - p0;
	Vector b = p2 - p0;

	if (cross(a, b) > EPS) return COUNTER_CLOCKWISE;
	if (cross(a, b) < -EPS) return CLOCKWISE;
	if (dot(a, b) < -EPS) return ONLINE_BACK;
	if (a.norm() < b.norm()) return ONLINE_FRONT;

	return ON_SEGMENT;
}
Polygon andrewScan(Polygon s){
	Polygon u, l;

	if (s.size() < 3) return s;

	sort(s.begin(), s.end()); //x,yを基準にソート

	u.push_back(s[0]);
	u.push_back(s[1]);

	l.push_back(s[s.size() - 1]);
	l.push_back(s[s.size() - 2]);

	//凸方の上部を生成
	for (int i = 2; i < s.size(); i++){
		for (int n = u.size(); n >= 2 && ccw(u[n - 2], u[n - 1], s[i]) == 1; n--){
			u.pop_back();
		}
		u.push_back(s[i]);
	}

	//凸法の下部を生成
	for (int i = s.size() - 3; i >= 0; i--){
		for (int n = l.size(); n >= 2 && ccw(l[n - 2], l[n - 1], s[i]) == 1; n--){
			l.pop_back();
		}
		l.push_back(s[i]);
	}

	//反時計回りになるように凸包の点の列
	reverse(l.begin(), l.end());
	for (int i = u.size() - 2; i >= 1; i--)
		l.push_back(u[i]);

	return l;
}

class UnionFind{
private:
  vector<int> par, rank;
public:
  UnionFind(int n){
	par.assign(n, 0);
	rank.assign(n, 0);
	for(int i=0;i<n;++i)
	  par[i] = i;
  }

  //find root of x
  int find(int x){
	if(par[x] == x)
	  return x;
	return (par[x] = find(par[x]));
  }

  void unite(int x, int y){
	x = find(x);
	y = find(y);
	if(x == y) return;

	if(rank[x] < rank[y])
	  par[x] = y;
	else{
	  par[y] = x;
	  if(rank[x] == rank[y])
		++rank[x];
	}
  }

  bool same(int x, int y){
	return find(x) == find(y);
  }
};

int main(){
  cin.tie(0);
  ios_base::sync_with_stdio(false);

  int V, R;
  cin >> V >> R;
  Polygon pol(V);
  REP(i,V){
	cin >> pol[i].x >> pol[i].y;
  }

  Polygon t = andrewScan(pol);
  vector<bool> used(V);
  REP(i,V){
	for(auto& p: t)
	  if(p == pol[i]){
		used[i] = true;
		break;
	  }
  }

  UnionFind uf(V);
  double ans = 0;
  REP(i,SZ(t)){
	ans += (t[i] - t[(i+1)%SZ(t)]).abs();
  }
  REP(i,V) REP(j,V)
	if(used[i] && used[j])
	  uf.unite(i, j);

  vector<pair<double,PII>> E(R);
  REP(i,R){
	cin >> E[i].SS.FF >> E[i].SS.SS;
	--E[i].SS.FF;
	--E[i].SS.SS;
	E[i].FF = (pol[E[i].SS.FF] - pol[E[i].SS.SS]).abs();
  }

  SORT(E);
  for(auto& e: E){
	if(uf.same(e.SS.FF, e.SS.SS)) continue;
	uf.unite(e.SS.FF, e.SS.SS);
	ans += e.FF;
  }

  cout << fixed << setprecision(9) << ans << endl;

  return 0;
}



Measured memory_bytes: 3272