#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using VI = vector<int>;
using VL = vector<ll>;
#define FOR(i,a,n) for(int (i)=(a);(i)<(n);(i)++)
#define eFOR(i,a,n) for(int (i)=(a);(i)<=(n);(i)++)
#define rFOR(i,a,n) for(int (i)=(n)-1;(i)>=(a);(i)--)
#define erFOR(i,a,n) for(int (i)=(n);(i)>=(a);(i)--)
#define SORT(i) sort((i).begin(),(i).end())
#define rSORT(i,a) sort((i).begin(),(i).end(),(a))
#define all(i) (i).begin(),(i).end()
constexpr ll INF = 1000000000;
constexpr ll LLINF = 1LL << 60;
constexpr ll mod = 1000000007;
constexpr ll MOD = 998244353;
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; }return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; }return 0; }
inline void init() { cin.tie(nullptr); cout.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(15); }
template<class T> inline istream& operator>>(istream& input, vector<T>& v) { for (auto& elemnt : v)input >> elemnt; return input; }

struct Point {
    double x, y;
    int num;
    bool operator <(const Point& p) const {
        return x < p.x || (x == p.x && y < p.y);
    }
};

double cross(const Point& O, const Point& A, const Point& B){
    return (A.x - O.x) * (B.y - O.y) - (A.y - O.y) * (B.x - O.x);
}

vector<Point> convex_hull(vector<Point> P){
    size_t n = P.size(), k = 0;
    if (n <= 3) return P;
    vector<Point> H(2 * n);

    // Sort points lexicographically
    sort(P.begin(), P.end());

    // Build lower hull
    for (size_t i = 0; i < n; ++i) {
        while (k >= 2 && cross(H[k - 2], H[k - 1], P[i]) <= 0) k--;
        H[k++] = P[i];
    }

    // Build upper hull
    for (size_t i = n - 1, t = k + 1; i > 0; --i) {
        while (k >= t && cross(H[k - 2], H[k - 1], P[i - 1]) <= 0) k--;
        H[k++] = P[i - 1];
    }

    H.resize(k - 1);
    return H;
}

vector<Point> g;

struct edge {
    int s, t; double dis;

    bool operator < (const edge& x) const {
        return dis < x.dis;
    }
};

double distance(int s, int t) {
    return sqrt(pow(g[s].x - g[t].x, 2) + pow(g[s].y - g[t].y, 2));
}

class unionfind {
    VI par, rank;
    int forest_num;
public:
    unionfind(int n) : par(n), rank(n, 1) {
        forest_num = n;
        for (int i = 0; i < n; i++)par[i] = i;
    }
    void init(int n) {
        par.clear(); par.resize(n);
        rank.clear(); rank.resize(n, 1);
        forest_num = n;
        for (int i = 0; i < n; i++)par[i] = i;
    }
    int root(int x) {
        if (par[x] == x)return x;
        return par[x] = root(par[x]);
    }
    int size(int x) {
        if (par[x] == x)return rank[x];
        return size(par[x]);
    }
    void unite(int x, int y) {
        int rx = root(x);
        int ry = root(y);
        if (rx == ry)return;
        if (rank[rx] < rank[ry]) {
            par[rx] = ry;
            rank[ry] += rank[rx];
        }
        else {
            par[ry] = rx;
            rank[rx] += rank[ry];
        }
        forest_num--;
    }
    bool same(int x, int y) {
        return root(x) == root(y);
    }
    int fnum() {
        return forest_num;
    }
};

int main() {
    init();
    
    int v, r;
    cin >> v >> r;
    g.resize(v);
    FOR(i, 0, v) {
        double x, y;
        cin >> x >> y;
        g[i] = { x,y,i };
    }

    vector<Point> con = convex_hull(g);
    unionfind uf(v);
    double ans = 0;
    eFOR(i, 1, con.size()) {
        uf.unite(con[i - 1].num, con[i % con.size()].num);
        ans += distance(con[i - 1].num, con[i % con.size()].num);
    }
    vector<edge> e(r);

    FOR(i, 0, r) {
        int s, t;
        cin >> s >> t;
        s--, t--;
        double dis = distance(s, t);
        e[i] = { s,t,dis };
    }
    SORT(e);

    for (auto i : e) {
        if (uf.same(i.s, i.t))continue;
        uf.unite(i.s, i.t);
        ans += i.dis;
    }
    cout << ans << "\n";
}


Measured memory_bytes: 3352