#include <bits/stdc++.h>

using namespace std;

#define FOR(i,a,b) for(int i=(a); i<(b); i++)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()
#define fi first
#define se second

template<typename A, typename B> inline bool chmax(A &a, B b) { if (a<b) { a=b; return 1; } return 0; }
template<typename A, typename B> inline bool chmin(A &a, B b) { if (a>b) { a=b; return 1; } return 0; }

typedef long long ll;
typedef pair<int, int> pii;
typedef complex<long double> P;
typedef complex<long double> Vec;
const ll INF = 1ll<<29;
const ll MOD = 1000000007;
const long double EPS = 1e-10;
const bool debug = 0;

struct UnionFind {
	vector<int> par, rank;
	
	UnionFind(int n) {
		par.resize(n);
		rank.resize(n);
		REP(i, n) {
			par[i] = i;
			rank[i] = 0;
		}
	}
	
	int operator[] (int x) {
		return find(x);
	}
	
	int find(int x) {
		if (x == par[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]) swap(x, y);
		
		par[y] = x;
		if (rank[x] == rank[y]) rank[x]++;
	}
	
	bool same(int x, int y) {
		return find(x) == find(y);
	}
};

long double cross(Vec a, Vec b) {
	return a.real() * b.imag() - a.imag() * b.real();
}

bool comp(pair<P, int> a, pair<P, int> b) {
	if (a.fi.real() != b.fi.real()) return a.fi.real() < b.fi.real();
	return a.fi.imag() < b.fi.imag();
}

vector<int> convex_full(vector<P> p) {
	vector<pair<P, int> > v(p.size());
	REP(i, p.size()) v[i] = make_pair(p[i], i);
	
	sort(ALL(v), comp);
	vector<P> res(2 * v.size());
	vector<int> num(2 * v.size());
	int k = 0;
	
	REP(i, p.size()) {
		while (k > 1 && cross(res[k - 1] - res[k - 2], v[i].fi - res[k - 2]) <= EPS) k--;
		res[k++] = v[i].fi;
		num[k - 1] = v[i].se;
	}
	
	for (int i = (int)p.size() - 2, c1 = k; i >= 0; i--) {
		while (k > c1 && cross(res[k - 1] - res[k - 2], v[i].fi - res[k - 2]) <= EPS) k--;
		res[k++] = v[i].fi;
		num[k - 1] = v[i].se;
	}
	
	res.resize(k - 1);
	num.resize(k - 1);
	return num;
}

int V, R;
vector<P> v;
int s[1000], t[1000];
vector<int> g[100];

int main() {
	cin >> V >> R;
	REP(i, V) {
		int x, y;
		scanf("%d %d", &x, &y);
		v.push_back(P(x, y));
	}
	
	REP(i, R) {
		scanf("%d %d", s + i, t + i);
		s[i]--; t[i]--;
		g[s[i]].push_back(t[i]);
		g[t[i]].push_back(s[i]);
	}
	
	vector<int> totu = convex_full(v);
	
	long double ans = 0.0;
	REP(i, totu.size()) {
		int a = totu[i];
		int b = totu[(i + 1) % totu.size()];
		ans += abs(v[a] - v[b]);
	}
	
	UnionFind uf(V);
	REP(i, totu.size() - 1) {
		uf.unite(totu[i], totu[i + 1]);
	}
	
	priority_queue<pair<long double, int>, vector<pair<long double, int> >, greater<pair<long double, int> > > pq;
	REP(i, totu.size()) {
		int u = totu[i];
		REP(j, g[u].size()) {
			int to = g[u][j];
			if (uf.same(u, to)) continue;
			pq.push(make_pair(abs(v[to] - v[u]), to));
		}
	}
	
	while (!pq.empty()) {
		long double l = pq.top().fi;
		int u = pq.top().se;
		pq.pop();
		
		if (uf.same(totu[0], u)) continue;
		
		REP(i, g[u].size()) {
			int to = g[u][i];
			if (uf.same(u, to)) continue;
			pq.push(make_pair(abs(v[to] - v[u]), to));
		}
		
		ans += l;
		uf.unite(totu[0], u);
	}
	
	printf("%.15lf\n", (double)ans);
	
	return 0;
}

Measured memory_bytes: 3424