#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <tuple>
using namespace std;

struct Point {
	long double px, py;
};
bool operator< (const Point &a1, const Point &a2) {
	if (a1.px < a2.px && fabs(a1.px - a2.px) >= 1e-7) return true;
	if (a1.px > a2.px && fabs(a1.px - a2.px) >= 1e-7) return false;
	if (a1.py < a2.py && fabs(a1.py - a2.py) >= 1e-7) return true;
	if (a1.py > a2.py && fabs(a1.py - a2.py) >= 1e-7) return false;
	return false;
}
int ccw(Point a1, Point a2, Point a3) {
	Point v1 = a2, v2 = a3;
	v1.px -= a1.px; v1.py -= a1.py;
	v2.px -= a1.px; v2.py -= a1.py;

	long double crs = v1.px * v2.py - v2.px * v1.py;
	long double dot = v1.px * v2.px + v1.py * v2.py;
	
	if (crs > 1e-7) return 1;
	if (crs < -1e-7) return -1;
	if (dot < -1e-7) return 2;
	if (a1.px * a1.px + a1.py * a1.py < a2.px * a2.px + a2.py * a2.py) return -2;
	return 0;
}

int N, M, a[1009], b[1009]; Point G[109]; vector<pair<Point, int>>vec;

vector<int> convex_hull() {
	if (vec.size() == 1) return vector<int>{vec[0].second};

	sort(vec.begin(), vec.end());

	vector<pair<Point, int>> V;
	V.push_back(vec[0]); V.push_back(vec[1]);
	for (int i = 2; i < vec.size(); i++) {
		while (V.size() >= 2) {
			if (ccw(V[V.size() - 2].first, V[V.size() - 1].first, vec[i].first) >= 0) V.pop_back();
			else break;
		}
		V.push_back(vec[i]);
	}
	reverse(vec.begin(), vec.end());

	vector<pair<Point, int>>W;
	W.push_back(vec[0]); W.push_back(vec[1]);
	for (int i = 2; i < vec.size(); i++) {
		while (W.size() >= 2) {
			if (ccw(W[W.size() - 2].first, W[W.size() - 1].first, vec[i].first) >= 0) W.pop_back();
			else break;
		}
		W.push_back(vec[i]);
	}

	vector<int> F;
	for (int i = 0; i < V.size(); i++) F.push_back(V[i].second);
	for (int i = 1; i < (int)W.size() - 1; i++) F.push_back(W[i].second);

	// 最終的には、Convex Hull に含まれる頂点番号を全て出力
	return F;
}

long double dst(Point a1, Point a2) {
	return sqrtl((a1.px - a2.px) * (a1.px - a2.px) + (a1.py - a2.py) * (a1.py - a2.py));
}

class UnionFind {
public:
	vector<int>dat;

	void init(int sz) {
		dat.resize(sz, -1);
	}
	int root(int pos) {
		if (dat[pos] == -1) return pos;
		dat[pos] = root(dat[pos]);
		return dat[pos];
	}
	void unite(int u, int v) {
		u = root(u); v = root(v);
		if (u == v) return;
		dat[u] = v;
	}
	bool same(int u, int v) {
		if (root(u) == root(v)) return true;
		return false;
	}
};

long double answer = 0; vector<tuple<long double, int, int>>V;

int main() {
	cin >> N >> M;
	for (int i = 0; i < N; i++) { cin >> G[i].px >> G[i].py; vec.push_back(make_pair(G[i], i)); }
	sort(vec.begin(), vec.end());

	vector<int> A = convex_hull();

	for (int i = 0; i < M; i++) {
		int a1, a2; cin >> a1 >> a2; a1--; a2--;
		long double res = dst(G[a1], G[a2]);
		V.push_back(make_tuple(res, a1, a2));
	}
	for (int i = 0; i < A.size(); i++) {
		answer += dst(G[A[i]], G[A[(i + 1) % A.size()]]);
		for (int j = 0; j < A.size(); j++) V.push_back(make_tuple(0.0L, A[i], A[j]));
	}
	sort(V.begin(), V.end());
	UnionFind UF; UF.init(N);
	for (int i = 0; i < V.size(); i++) {
		if (UF.same(get<1>(V[i]), get<2>(V[i])) == false) {
			UF.unite(get<1>(V[i]), get<2>(V[i]));
			answer += get<0>(V[i]);
		}
	}
	printf("%.12Lf\n", answer);
	return 0;
}


Measured memory_bytes: 3472