#include <bits/stdc++.h>
using namespace std;
 
#define REP(i, n) for (ll i = 0; i < n; i++)
#define REPR(i, n) for (ll i = n; i >= 0; i--)
#define FOR(i, m, n) for (ll i = m; i < n; i++)
#define FORR(i, m, n) for (ll i = m; i >= n; i--)
#define REPO(i, n) for (ll i = 1; i <= n; i++)
#define ll long long
#define INF (ll)1 << 60
#define MINF (-1 * INF)
#define ALL(n) n.begin(), n.end()
#define MOD 1000000007
//#define P pair<ll, ll>
#define Pa pair<ll, ll>

double EPS = 1e-10;

double add(double a, double b) {
    if (abs(a + b) < EPS * (abs(a) + abs(b))) return 0;
    return a + b;
}

struct P {
    double x, y;
    P() {}
    P(double x, double y) : x(x), y(y) {
    }
    P operator+(P p) {
        return P(add(x, p.x), add(y, p.y));
    }
    P operator-(P p) {
        return P(add(x, -p.x), add(y, -p.y));
    }
    P operator*(double d) {
        return P(x * d, y * d);
    }
    double dot(P p) {
        return add(x * p.x, y * p.y);
    }
    double det(P p) {
        return add(x * p.y, -y * p.x);
    }
};

bool cmp_x(const P &p, const P &q) {
    if (p.x != q.x) return p.x < q.x;
    return p.y < q.y;
}

vector<P> convex_hull(P *ps, int n) {
    sort(ps, ps + n, cmp_x);
    int k = 0;
    vector<P> qs(n * 2);
    REP(i, n) {
        while (k > 1 and (qs[k - 1] - qs[k - 2]).det(ps[i] - qs[k - 1]) <= 0) k--;
        qs[k++] = ps[i];
    }
    for (int i = n - 2, t = k; i >= 0; i--) {
        while (k > t and (qs[k - 1] - qs[k - 2]).det(ps[i] - qs[k - 1]) <= 0) k--;
        qs[k++] = ps[i];
    }
    qs.resize(k - 1);
    return qs;
}
double dist(P p, P q) {
    return (p - q).dot(p - q);
}

struct UnionFind {
    vector<int> data;
    UnionFind(int size) : data(size, -1) {}
    bool unionSet(int x, int y) {
        x = root(x);
        y = root(y);
        if (x != y) {
            if (data[y] < data[x]) swap(x, y);
            data[x] += data[y];
            data[y] = x;
        }
        return x != y;
    }
    bool findSet(int x, int y) {
        return root(x) == root(y);
    }
    int root(int x) {
        return data[x] < 0 ? x : data[x] = root(data[x]);
    }
    int size(int x) {
        return -data[root(x)];
    }
};

struct Edge {
    int a, b;
    double cost;
    bool operator<(const Edge &o) const {
        return cost < o.cost;
    }
};

vector<Edge> es;
UnionFind uf(210000);
double kruskal() {
    sort(ALL(es));
    double min_cost = 0;
    REP(ei, es.size()) {
        Edge &e = es[ei];
        if (!uf.findSet(e.a, e.b)) {
            min_cost += e.cost;
            uf.unionSet(e.a, e.b);
        }
    }
    return min_cost;
}


ll n, m, used[110][110];
double ans;
P ps[210000], ts[210000];
map<Pa, ll> mp;
int main() {
    cin >> n >> m;
    
    REP(i, n) {
        ll a, b;
        cin >> a >> b;
        ts[i] = ps[i] = {(double)a, (double)b};
        mp[Pa(a, b)] = i;
    }
    vector<P> qs = convex_hull(ps, n);
    qs.push_back(qs[0]);
    vector<ll> t;
    REP(i, qs.size()) {
        //cout << qs[i].x << " " << qs[i].y << " " << mp[Pa(qs[i].x, qs[i].y)]<< endl;
        t.push_back(mp[Pa(qs[i].x, qs[i].y)]);
    }
    REP(i, t.size() - 1) {
        used[t[i]][t[i + 1]] = true;
        used[t[i + 1]][t[i]] = true;
        uf.unionSet(t[i], t[i + 1]);
        ans += sqrt(dist(ts[t[i]], ts[t[i + 1]]));
    }
    REP(i, m) {
        ll a, b;
        cin >> a >> b;
        a--;
        b--;
        if (uf.findSet(a, b)) continue;
        if (used[a][b]) {
            /*
            uf.unionSet(a, b);
            ans += sqrt(dist(ts[a], ts[b]));
            cout << a << " " << b << " !!!!" << sqrt(dist(ts[a], ts[b])) << endl;
            cout << ts[a].x << " " << ts[a].y << " " << ts[b].x << " " << ts[b].y << endl;
            */
        }
        else{
            es.push_back({a, b, sqrt(dist(ts[a], ts[b]))});
        }
    }
   //cout << ans << endl;
    cout << fixed << setprecision(10) << ans + kruskal() << endl;
}


Measured memory_bytes: 3956