Problem:
Problem D
  Making Perimeter of the Convex Hull Shortest
The convex hull of a set of three or more planar points is, when not all of them are on one line, the convex polygon with the smallest area that has all the points of the set on its boundary or in its inside. Your task is, given positions of the points of a set, to find how much shorter the perimeter of the convex hull can be made by excluding
two
points from the set.
The figures below correspond to the three cases given as Sample Input 1 to 3. Encircled points are excluded to make the shortest convex hull depicted as thick dashed lines.
Sample Input 1
Sample Input 2
Sample Input 3
Input
The input consists of a single test case in the following format.
$n$
$x_1$ $y_1$
...
$x_n$ $y_n$
Here, $n$ is the number of points in the set satisfying $5 \leq n \leq 10^5$. For each $i$, ($x_i, y_i$) gives the coordinates of the position of the $i$-th point in the set. $x_i$ and $y_i$ are integers between $-10^6$ and $10^6$, inclusive. All the points in the set are distinct, that is, $x_j \ne x_k$ or $y_j \ne y_k$ holds when $j \ne k$. It is guaranteed that no single line goes through $n - 2$ or more points in the set.
Output
Output the difference of the perimeter of the convex hull of the original set and the shortest of the perimeters of the convex hulls of the subsets with two points excluded from the original set. The output should not have an error greater than $10^{-4}$.
Sample Input 1
10
-53 62
-19 58
-11 11
-9 -22
45 -7
37 -39
47 -58
-2 41
-37 10
13 42
Sample Output 1
72.96316928
Sample Input 2
10
-53 62
-19 58
-11 11
-9 -22
45 -7
43 -47
47 -58
-2 41
-37 10
13 42
Sample Output 2
62.62947992
Sample Input 3
10
-53 62
-35 47
-11 11
-9 -22
45 -7
43 -47
47 -58
-2 41
-37 10
13 42
Sample Output 3
61.58166534

Submitted solutions:

code_snippet: // #define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = 1LL << 60;
#define rep(i, a) for(ll i = 0; i < (a); i++)
#define all(a) begin(a), end(a)
#define sz(a) (a).size()
template<class T> inline bool chmin(T& a, T b) {if(a > b){a = b; return true;} else {return false;}};
template<class T> inline bool chmax(T& a, T b) {if(a < b){a = b; return true;} else {return false;}};
void out() { cout << endl; }
template<class T, class... Ts> void out(const T& a, const Ts&... b){ cout<<a; (cout<<... << (cout << ' ', b)); cout << endl;}
using vll = vector<ll>;
using vvll = vector<vll>;
using pll = pair<ll, ll>;
using ld = long double;

int main()
{
    cout << fixed << setprecision(20);
    ll N; cin >> N;
    vector<pll> XY(N);
    rep(i, N) cin >> XY[i].first >> XY[i].second;
    sort(all(XY));
    vll up, lw;
    auto is_ccw = [&](ll i, ll j, ll k)
    {
        ll x1 = (XY[k].first - XY[j].first), y1 = (XY[k].second - XY[j].second);
        ll x2 = (XY[i].first - XY[j].first), y2 = (XY[i].second - XY[j].second);
        ll v = x1*y2 - x2*y1;
        if(v > 0) return 1;
        else if(v < 0) return -1;
        else return 0;
    };
    auto dist = [&](ll i, ll j)
    {
        return hypotl(XY[i].first - XY[j].first, XY[i].second - XY[j].second);
    };
    for(ll i = 0; i < N; i++)
    {
        while(up.size() >= 2 && is_ccw(up[up.size() - 2], up.back(), i) != -1) up.pop_back();
        up.push_back(i);
        while(lw.size() >= 2 && is_ccw(lw[lw.size() - 2], lw.back(), i) != 1) lw.pop_back();
        lw.push_back(i);
    }
    // rep(i, up.size() - 1)
    // {
    //     cout << "(" << XY[up[i]].first << "," << XY[up[i]].second << "),";
    // }
    // for(ll i = lw.size() - 1; i >= 1; i--)
    // {
    //     cout << "(" << XY[lw[i]].first << "," << XY[lw[i]].second << "),";
    // }
    // cout << endl;
    // for(auto i : up) cout << i << " ";
    // out("");
    // for(auto i : lw) cout << i << " ";
    // out("");
    ld orglen = 0;
    rep(i, up.size() - 1) orglen += dist(up[i], up[i + 1]);
    rep(i, lw.size() - 1) orglen += dist(lw[i], lw[i + 1]);
    vector<ld> erase1;
    auto calc_erase_up = [&](ll l, ll r, ll e1, ll e2 = -1)
    {
        vll tp;
        for(ll i = l; i <= r; i++)
        {
            if(i == e1 || i == e2) continue;
            while(tp.size() >= 2 && is_ccw(tp[tp.size() - 2], tp.back(), i) != -1) tp.pop_back();
            tp.push_back(i);
        }
        return tp;
    };
    auto calc_erase_lw = [&](ll l, ll r, ll e1, ll e2 = -1)
    {
        vll tw;
        for(ll i = l; i <= r; i++)
        {
            if(i == e1 || i == e2) continue;
            while(tw.size() >= 2 && is_ccw(tw[tw.size() - 2], tw.back(), i) != 1) tw.pop_back();
            tw.push_back(i);
        }
        return tw;
    };
    ld ans = 0;
    {
        auto up1 = calc_erase_up(0, N-1, up[0]);
        auto lw1 = calc_erase_lw(0, N-1, up[0]);
        ld adt = orglen;
        rep(i, up1.size() - 1) adt -= dist(up1[i], up1[i + 1]);
        rep(i, lw1.size() - 1) adt -= dist(lw1[i], lw1[i + 1]);
        erase1.push_back(adt);
        for(ll i = 1; i < up1.size() - 1; i++)
        {
            {
                auto u = calc_erase_up(up1[i - 1], up1[i + 1], up[0], up1[i]);
                ld ad = dist(up1[i - 1], up1[i]) + dist(up1[i], up1[i + 1]);
                rep(j, u.size() - 1) ad -= dist(u[j], u[j + 1]);
                chmax(ans, adt + ad);
            }
        }
        for(ll i = 1; i < lw1.size() - 1; i++)
        {
            {
                auto d = calc_erase_lw(lw1[i - 1], lw1[i + 1], up[0], lw1[i]);
                ld ad = dist(lw1[i], lw1[i + 1]) + dist(lw1[i - 1], lw1[i]);
                rep(j, d.size() - 1) ad -= dist(d[j], d[j + 1]);
                chmax(ans, adt + ad);
            }
        }
        {
            auto up2 = calc_erase_up(0, N-1, up[0], up1[0]);
            auto lw2 = calc_erase_lw(0, N-1, up[0], up1[0]);
            ld ad = orglen;
            rep(i, up2.size() - 1) ad -= dist(up2[i], up2[i + 1]);
            rep(i, lw2.size() - 1) ad -= dist(lw2[i], lw2[i + 1]);
            chmax(ans, ad);
        }
        {
            auto up2 = calc_erase_up(0, N-1, up[0], up1.back());
            auto lw2 = calc_erase_lw(0, N-1, up[0], up1.back());
            ld ad = orglen;
            rep(i, up2.size() - 1) ad -= dist(up2[i], up2[i + 1]);
            rep(i, lw2.size() - 1) ad -= dist(lw2[i], lw2[i + 1]);
            chmax(ans, ad);
        }
    }
    for(ll i = 1; i < up.size() - 1; i++)
    {
        {
            auto u = calc_erase_up(up[i - 1], up[i + 1], up[i]);
            ld ad = dist(up[i - 1], up[i]) + dist(up[i], up[i + 1]);
            rep(j, u.size() - 1) ad -= dist(u[j], u[j + 1]);
            erase1.push_back(ad);
            if(up[i + 1] != up.back())
            {
                auto u2 = calc_erase_up(up[i - 1], up[i + 2], up[i], up[i + 1]);
                ld ad2 = dist(up[i - 1], up[i]) + dist(up[i], up[i + 1]) + dist(up[i + 1], up[i + 2]);
                rep(j, u2.size() - 1) ad2 -= dist(u2[j], u2[j + 1]);
                chmax(ans, ad2);
            }
            for(ll j = 1; j < u.size() - 1; j++)
            {
                auto uu = calc_erase_up(u[j - 1], u[j + 1], up[i], u[j]);
                ld ad2 = dist(u[j - 1], u[j]) + dist(u[j], u[j + 1]);
                rep(k, uu.size() - 1) ad2 -= dist(uu[k], uu[k + 1]);
                chmax(ans, ad + ad2);
            }
        }
    }
    {
        auto up1 = calc_erase_up(0, N-1, up.back());
        auto lw1 = calc_erase_lw(0, N-1, up.back());
        ld adt = orglen;
        rep(i, up1.size() - 1) adt -= dist(up1[i], up1[i + 1]);
        rep(i, lw1.size() - 1) adt -= dist(lw1[i], lw1[i + 1]);
        erase1.push_back(adt);
        for(ll i = 1; i < up1.size() - 1; i++)
        {
            {
                auto u = calc_erase_up(up1[i - 1], up1[i + 1], up.back(), up1[i]);
                ld ad = dist(up1[i - 1], up1[i]) + dist(up1[i], up1[i + 1]);
                rep(j, u.size() - 1) ad -= dist(u[j], u[j + 1]);
                chmax(ans, adt + ad);
            }
        }
        for(ll i = 1; i < lw1.size() - 1; i++)
        {
            {
                auto d = calc_erase_lw(lw1[i - 1], lw1[i + 1], up.back(), lw1[i]);
                ld ad = dist(lw1[i], lw1[i + 1]) + dist(lw1[i - 1], lw1[i]);
                rep(j, d.size() - 1) ad -= dist(d[j], d[j + 1]);
                chmax(ans, adt + ad);
            }
        }
        {
            auto up2 = calc_erase_up(0, N-1, up.back(), up1[0]);
            auto lw2 = calc_erase_lw(0, N-1, up.back(), up1[0]);
            ld ad = orglen;
            rep(i, up2.size() - 1) ad -= dist(up2[i], up2[i + 1]);
            rep(i, lw2.size() - 1) ad -= dist(lw2[i], lw2[i + 1]);
            chmax(ans, ad);
        }
        {
            auto up2 = calc_erase_up(0, N-1, up.back(), up1.back());
            auto lw2 = calc_erase_lw(0, N-1, up.back(), up1.back());
            ld ad = orglen;
            rep(i, up2.size() - 1) ad -= dist(up2[i], up2[i + 1]);
            rep(i, lw2.size() - 1) ad -= dist(lw2[i], lw2[i + 1]);
            chmax(ans, ad);
        }
    }
    for(ll i = lw.size() - 2; i >= 1; i--)
    {
        {
            auto d = calc_erase_lw(lw[i - 1], lw[i + 1], lw[i]);
            ld ad = dist(lw[i], lw[i + 1]) + dist(lw[i - 1], lw[i]);
            rep(j, d.size() - 1) ad -= dist(d[j], d[j + 1]);
            erase1.push_back(ad);
            if(lw[i + 1] != lw.back())
            {
                auto d2 = calc_erase_lw(lw[i - 1], lw[i + 2], lw[i], lw[i + 1]);
                ld ad2 = dist(lw[i], lw[i + 1]) + dist(lw[i - 1], lw[i]) + dist(lw[i + 1], lw[i + 2]);
                rep(j, d2.size() - 1) ad2 -= dist(d2[j], d2[j + 1]);
                chmax(ans, ad2);
            }
            for(ll j = 1; j < d.size() - 1; j++)
            {
                auto dd = calc_erase_lw(d[j - 1], d[j + 1], lw[i], d[j]);
                ld ad2 = dist(d[j - 1], d[j]) + dist(d[j], d[j + 1]);
                rep(k, dd.size() - 1) ad2 -= dist(dd[k], dd[k + 1]);
                chmax(ans, ad + ad2);
            }
        }
    }
    // out(orglen);
    // out(ans);
    // for(auto e : erase1) cout << e << " ";
    // out();
    vector<ld> Lmx(erase1.size() + 1, -INF), Rmx(erase1.size() + 1, -INF);
    for(ll i = 1; i < erase1.size(); i++)
    {
        Lmx[i + 1] = max(Lmx[i], erase1[i]);
    }
    for(ll i = erase1.size() - 1; i >= 0; i--)
    {
        Rmx[i] = max(Rmx[i + 1], erase1[i]);
    }
    for(ll i = 1; i < erase1.size(); i++)
    {
        ld mx = -INF;
        if(i - 1 >= 0) chmax(mx, Lmx[i - 1]);
        if(i + 2 <= erase1.size()) chmax(mx, Rmx[i + 2]);
        if(mx == -INF) continue;
        chmax(ans, (mx + erase1[i]));
    }
    out(ans);
}
accuracy: 1
time_ms: 70
memory_kb: 6144
final_rank: 5

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
#include <cassert>
#include <cmath>
using namespace std;
#define rep(i,n) for(int i=0; i<(int)(n); i++)
using ll = long long;
template <class T> using V=vector<T>;
template <class T>
void chmin(T& l, const T& r){ if(r<l) l=r; }

struct P{
    ll x,y;
    ll operator^(P r) const {
        return x * r.y - y * r.x;
    }
    P operator-(P r) const {
        return {x-r.x, y-r.y};
    }
    bool operator<(P r) const {
        if(x==r.x) return y<r.y;
        return x<r.x;
    }

    double len() const {
        return sqrt((double)(x*x+y*y));
    }
    double dist(P r)const {
        P f = *this - r;
        return f.len();
    }
};

V<int> hull(V<P> p){
    V<int> s;
    rep(z,p.size()){
        while(s.size() >= 2){
            int y = s.back();s.pop_back();
            int x =s.back();
            if(((p[y]-p[x])^(p[z]-p[x]))>0){
                s.push_back(y); break;
            }
        }
        s.push_back(z);
    }
    return s;
}

template<class A> V<A> sub(const V<A>& a, int l, int r){
    return V<A>(
        a.begin()+l, a.begin()+r
    );
}
template<class A>
V<A> cat(V<A> l, const V<A>& r){
    l.insert(l.end(), r.begin(), r.end());
    return l;
}

V<double> solve_lower(V<P> A, int lim){
    auto h = hull(A);
    int z = h.size();
    V<V<double>> dp(lim+1, V<double>(z,1.e100));
    rep(i,lim+1) dp[i][0] = 0;
    rep(s,z-1){
        V<P> a = sub(A,h[s],h[s+1]+1);
        rep(st,lim+1) if(s+st+1<z){
            int t = s + st + 1;
            if(st==0){
                rep(k,lim+1) chmin(dp[k][t], dp[k][s]+A[h[s]].dist(A[h[t]]));
                continue;
            }
            a.pop_back();
            a = cat(move(a), sub(A,h[t-1]+1,h[t]+1));
            auto sh = solve_lower(a,lim-st);
            rep(i,lim-st+1) rep(j,lim+1) if(i+j+st<=lim)
                chmin(dp[j+i+st][t], dp[j][s] + sh[i]);
        }
    }
    V<double> res(lim+1);
    rep(i,lim+1) res[i]=dp[i][z-1];
    return res;
}

double solve_both(V<P> a, int k){
    auto x = solve_lower(a,k);
    reverse(a.begin(), a.end());
    auto y = solve_lower(a,k);
    double ans=1.e100;
    rep(i,k+1) chmin(ans, x[i]+y[k-i]);
    return ans;
}

double solve(V<P> a, int k){
    int n = a.size();
    double ans = 1.e100;
    rep(l,k+1) rep(r,k-l+1){
        chmin(ans,solve_both(sub(a,l,n-r), k-l-r));
    }
    return ans;
}

void testcase(){
    cout.precision(20);
    cout<<fixed;
    int n; cin >> n;
    V<P> a(n);
    for(auto& [x,y]:a) cin >> x >> y;
    sort(a.begin(),a.end());
    auto ans = solve(a,0)-solve(a,2);
    cout<<ans<<endl;
}

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    testcase();
    return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 12968
final_rank: 7

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

using DB = double;
struct PT {
	int x, y;
	PT (int x = 0, int y = 0) : x(x), y(y) {}
	void in() { scanf("%d%d", &x, &y); }
	bool operator <(const PT &pts) const { return make_pair(x, y) < make_pair(pts.x, pts.y); }
	bool operator ==(const PT &pts) const { return make_pair(x, y) == make_pair(pts.x, pts.y); }
};

DB sqr(int x) { return (DB)x * x; }

DB dis(PT p1, PT p2) {
	return sqrt(sqr(p1.x - p2.x) + sqr(p1.y - p2.y));
}

long long vect(PT p, PT p1, PT p2) {
	return 1LL * (p1.x - p.x) * (p2.y - p.y) - 1LL * (p1.y - p.y) * (p2.x - p.x);
}

long long scal(PT p, PT p1, PT p2) {
	return 1LL * (p1.x - p.x) * (p2.x - p.x) + 1LL * (p1.y - p.y) * (p2.y - p.y);
}

bool check(PT p1, PT p2, PT p3) {
	if (vect(p1, p2, p3) < 0) return 1;
	if (!vect(p1, p2, p3)) return scal(p2, p1, p3) <= 0;
	return 0;
}

vector<PT> pts, cvx;

void convex() {
	sort(pts.begin(), pts.end());
	pts.erase(unique(pts.begin(), pts.end()), pts.end());
	if (pts.size() == 1) {
		cvx.push_back(pts[0]);
		return;
	}
	for (int times = 0; times < 2; times++) {
		for (auto t : pts) {
			while (cvx.size() > 1 && check(cvx[cvx.size() - 2], cvx.back(), t)) cvx.pop_back();
			cvx.push_back(t);
		}
		reverse(pts.begin(), pts.end());
	}
	cvx.pop_back();
}

int getId(PT p) {
	return lower_bound(pts.begin(), pts.end(), p) - pts.begin();
}

vector<PT> getConvex(PT lft, PT cur, PT rht, PT nV) {
	vector<PT> nC;
	int px = getId(lft), cx = getId(cur), rx = getId(rht);
	int now = px;
	while (now != cx) {
		if (!(pts[now] == nV)) {
			while (nC.size() > 1 && check(nC[nC.size() - 2], nC.back(), pts[now])) nC.pop_back();
			nC.push_back(pts[now]);
		}
		if (now < cx) ++now;
		else --now;
	}
	while (now != rx) {
		if (now < rx) ++now;
		else --now;
		if (!(pts[now] == nV)) {
			while (nC.size() > 1 && check(nC[nC.size() - 2], nC.back(), pts[now])) nC.pop_back();
			nC.push_back(pts[now]);
		}
	}
	return nC;
}

vector<DB> profit;
multiset<DB> PM;
DB ans;

void process() {
	int sz = cvx.size();
	for (int i = 0; i < cvx.size(); i++) {
		PT lft = cvx[(i - 1 + sz) % sz], cur = cvx[i], rht = cvx[(i + 1) % sz];
		DB lose = dis(lft, cur) + dis(cur, rht);
		vector<PT> nC = getConvex(lft, cur, rht, PT(1e8, 1e8));
		DB get = 0;
		for (int j = 1; j < nC.size(); j++) get += dis(nC[j - 1], nC[j]);
		profit.push_back(lose - get);
		PM.insert(lose - get);
		nC.push_back(cvx[(i + 2) % sz]);
		for (int j = 1; j < (int)nC.size() - 1; j++) {
			PT _lft = nC[j - 1], _cur = nC[j], _nxt = nC[j + 1];
			DB _lose = dis(_lft, _cur) + dis(_cur, _nxt);
			vector<PT> _nC = getConvex(_lft, _cur, _nxt, cur);
			DB _get = 0;
			for (int k = 1; k < _nC.size(); k++) _get += dis(_nC[k - 1], _nC[k]);
			ans = max(ans, lose - get + _lose - _get);
		}
	}
	for (int i = 0; i < profit.size(); i++) {
		DB l = profit[(i - 1 + sz) % sz], c = profit[i], r = profit[(i + 1) % sz];
		PM.erase(PM.find(l)), PM.erase(PM.find(r)), PM.erase(PM.find(c));
		if (PM.size()) ans = max(ans, c + *(PM.rbegin()));
		else ans = max(ans, c);
		PM.insert(l), PM.insert(r), PM.insert(c);
	}
}

int main() {
	int n; scanf("%d", &n);
	PT p;
	while (n--) p.in(), pts.push_back(p);
	convex();
	process();
	printf("%.10lf\n", ans);
}
accuracy: 1
time_ms: 40
memory_kb: 4720
final_rank: 2

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


enum Type{
	Ue,
	Shita,
};

struct Point{
	Point(){

		X = Y = 0;
		x = y = 0;
		index = 0;
	}
	Point(ll arg_X,ll arg_Y){
		X = arg_X;
		Y = arg_Y;
		x = arg_X;
		y = arg_Y;
		index = 0;
	}

	bool operator<(const struct Point& arg) const{
		if(X != arg.X){

			return X < arg.X;
		}else{

			return Y < arg.Y;
		}
	}
	void debug(){

		printf("%lld %lld\n",X,Y);
	}

	ll X,Y;
	double x,y;
	int index;
};

struct POINT{
	POINT(double arg_x,double arg_y){
		x = arg_x;
		y = arg_y;
	}

	POINT(){
		x = y = 0.0;
	}

	POINT operator + (POINT p){ return POINT(x+p.x,y+p.y); }
	POINT operator - (POINT p){ return POINT(x-p.x,y-p.y);}
	POINT operator * (double a){ return POINT(a*x,a*y); }
	POINT operator / (double a){ return POINT(x/a,y/a); }

	double abs(){ return sqrt(norm()); }
	double norm(){ return x*x + y*y; }

	bool operator<(const POINT &p) const{

		if(fabs(x-p.x) > EPS){

			return x < p.x;
		}else{

			return y < p.y;
		}
	}

	bool operator == (const POINT &p) const{
		return fabs(x-p.x) < EPS && fabs(y-p.y) < EPS;
	}


	void debug(){

		printf("(%.3lf,%.3lf)\n",x,y);
	}

	double x,y;
};

typedef Point Vector;
typedef POINT VECTOR;
typedef vector<Point> Polygon;

struct Line{
	Line(){

	}
	Line(Point a,Point b){
		p[0] = a;
		p[1] = b;
	}
	/*void outPut(){

		printf("(%.3lf,%.3lf)-(%.3lf,%.3lf)\n",p[0].x,p[0].y,p[1].x,p[1].y);
	}*/
	Point p[2];
};

struct Info{
	Info(){

		del_P = 0;
		minus = 0;
	}
	Info(int arg_del_P,double arg_minus){
		del_P = arg_del_P;
		minus = arg_minus;
	}
	bool operator<(const struct Info &arg) const{

		return minus > arg.minus;
	}
	int del_P;
	double minus;
};

struct DATA{
	DATA(Point arg_p,Type arg_type){
		p = arg_p;
		type = arg_type;
	}
	DATA(){
		type = Ue;
	}

	Point p;
	Type type;
};

int N;
Polygon POLY;
vector<DATA> CH;

double calc_dist(Point A,Point B){

	return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}

double norm(Vector a){
	return a.x*a.x+a.y*a.y;
}

double abs(Vector a){
	return sqrt(norm(a));
}

double cross(Vector a,Vector b){
    return a.x*b.y-a.y*b.x;
}

double dot(Vector a,Vector b){
    return a.x*b.x + a.y*b.y;
}

//trueなら凸
bool calcGaiseki(Point left,Point base, Point right){
	ll tmp = (left.X-base.X)*(right.Y - base.Y)-(left.Y-base.Y)*(right.X-base.X);
	if(tmp < 0){
		return false;

	}else{
		return true;
	}
}


double norm(VECTOR a){
	return a.x*a.x+a.y*a.y;
}

double abs(VECTOR a){
	return sqrt(norm(a));
}

double cross(VECTOR a,VECTOR b){
    return a.x*b.y-a.y*b.x;
}

double dot(VECTOR a,VECTOR b){
    return a.x*b.x + a.y*b.y;
}

double calc_dist(POINT A,POINT B){

	return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}

static const int COUNTER_CLOCKWISE = 1;
static const int CLOCKWISE = -1;
static const int ONLINE_BACK = 2;
static const int ONLINE_FRONT = -2;
static const int ON_SEGMENT = 0;

/*
 * p0を基準点として、p1から見てp2が
 * 反時計回り側にあれば
 * COUNTER_CLOCKWISE
 * */
int ccw(POINT p0,POINT p1,POINT p2){

	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;
}

double ConvexHull(vector<POINT> V){

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

	vector<POINT> UP,DOWN;

	UP.push_back(V[0]);
	DOWN.push_back(V[0]);

	for(int i = 1; i < V.size(); i++){

		while(UP.size() > 1 && ccw(UP[UP.size()-2],UP[UP.size()-1],V[i]) == COUNTER_CLOCKWISE){

			UP.pop_back();
		}

		while(DOWN.size() > 1 && ccw(DOWN[DOWN.size()-2],DOWN[DOWN.size()-1],V[i]) ==  CLOCKWISE){

			DOWN.pop_back();
		}

		UP.push_back(V[i]);
		DOWN.push_back(V[i]);
	}

	vector<POINT> ret;

	for(int i = 0; i < UP.size(); i++){

		ret.push_back(UP[i]);
	}

	for(int i = DOWN.size()-1; i >= 0; i--){

		ret.push_back(DOWN[i]);
	}

	double tmp_dist = 0;
	for(int i = 0; i < ret.size(); i++){
		tmp_dist += calc_dist(ret[i],ret[(i+1)%ret.size()]);

	}

	return tmp_dist;
}



//left～rightの間、del1とdel2を使わずに凸包を求め、その途中の点を返却する関数
//left,rightはCH上のindexを受け取る
vector<Point> calc(int left,int right,int del1,int del2){

	vector<Point> TMP,ret;

	if(CH[left].type == Ue){

		if(CH[right].type == Ue){

			if(CH.size() <= 3 || left < right){

				for(int i = CH[left].p.index+1; i <= CH[right].p.index; i++){ //★ちょうどrightで終わるようにする★
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;

					TMP.push_back(POLY[i]);
				}

			}else{ //left > right

				for(int i = CH[left].p.index-1; i >= 0; i--){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;

					TMP.push_back(POLY[i]);
				}
				for(int i = 0; i <= CH[right].p.index; i++){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;

					TMP.push_back(POLY[i]);
				}
			}
		}else{ //CH[right].type == Shita

			for(int i = CH[left].p.index+1; i < N; i++){
				if(POLY[i].index == del1 || POLY[i].index == del2)continue;

				TMP.push_back(POLY[i]);
			}
			for(int i = N-1; i >= CH[right].p.index; i--){
				if(POLY[i].index == del1 || POLY[i].index == del2)continue;

				TMP.push_back(POLY[i]);
			}

		}

	}else{ //CH[left],type == Shita

		if(CH[right].type == Ue){

			for(int i = CH[left].p.index-1; i >= 0; i--){
				if(POLY[i].index == del1 || POLY[i].index == del2)continue;

				TMP.push_back(POLY[i]);
			}
			for(int i = 0; i <= CH[right].p.index; i++){
				if(POLY[i].index == del1 || POLY[i].index == del2)continue;

				TMP.push_back(POLY[i]);
			}
		}else{ //CH[right].type == Shita

			if(CH.size() <= 3 || left < right){

				for(int i = CH[left].p.index-1; i >= CH[right].p.index; i--){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;

					TMP.push_back(POLY[i]);
				}
			}else{

				for(int i = CH[left].p.index-1; i >= 0; i--){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;
					TMP.push_back(POLY[i]);
				}
				for(int i = 0; i <= CH[right].p.index; i++){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;
					TMP.push_back(POLY[i]);
				}
			}
		}
	}

	ret.push_back(POLY[CH[(left-1+CH.size())%CH.size()].p.index]);
	ret.push_back(POLY[CH[left].p.index]);


	for(int i = 0; i < TMP.size(); i++){
		if(TMP[i].index == ret.back().index)continue;
		while(ret.size() > 1 && calcGaiseki(ret[ret.size()-2],ret[ret.size()-1],TMP[i]) == false)ret.pop_back();
		ret.push_back(TMP[i]);
	}

	vector<Point> work;

	for(int i = 2; i < ret.size()-1; i++){

		work.push_back(ret[i]);
	}

	return work;
}

double calc_dist(int left,int right,vector<Point> vec){

	double ret = 0;
	if(vec.size() > 0){

		ret += calc_dist(CH[left].p,vec[0])+calc_dist(CH[right].p,vec[vec.size()-1]);
		for(int i = 1; i < vec.size(); i++){

			ret += calc_dist(vec[i],vec[i-1]);
		}
	}else{

		ret = calc_dist(CH[left].p,CH[right].p);
	}

	return ret;
}



int main(){


	scanf("%d",&N);

	ll X,Y;
	for(int i = 0; i < N; i++){

		scanf("%lld %lld",&X,&Y);
		POLY.push_back(Point(X,Y));
	}

	sort(POLY.begin(),POLY.end());
	for(int i = 0; i < N; i++){

		POLY[i].index = i; //sortした中での何番目か
	}

	Polygon V = POLY;

	vector<Point> UP,DOWN;

	UP.push_back(V[0]);
	UP.push_back(V[1]);

	DOWN.push_back(V[V.size()-1]);
	DOWN.push_back(V[V.size()-2]);

	for(int i = 2; i < V.size(); i++){
		while(UP.size() > 1 && calcGaiseki(UP[UP.size()-2],UP[UP.size()-1],V[i]) == false)UP.pop_back();
		UP.push_back(V[i]);
	}

	for(int i = V.size()-3; i >= 0; i--){
		while(DOWN.size() > 1 && calcGaiseki(DOWN[DOWN.size()-2],DOWN[DOWN.size()-1],V[i]) == false){
			DOWN.pop_back();
		}
		DOWN.push_back(V[i]);
	}

	for(int i =0; i < UP.size()-1;i++){

		CH.push_back(DATA(UP[i],Ue));
	}
	for(int i = 0; i < DOWN.size()-1 ;i++){

		CH.push_back(DATA(DOWN[i],Shita));
	}

	int num_P = CH.size();

	double sum = 0;
	for(int i = 0; i < CH.size(); i++){

		sum += calc_dist(CH[i].p,CH[(i+1)%num_P].p);
	}

	if(N <= 500){

		double ans = 0;

		for(int i = 0; i < N; i++){
			for(int k = 0; k < N; k++){
				if(k == i)continue;
				vector<POINT> P;
				for(int a = 0; a < N; a++){
					if(a == i || a == k)continue;

					P.push_back(POINT(POLY[a].x,POLY[a].y));
				}
				ans = max(ans,sum-ConvexHull(P));
			}
		}

		printf("%.10lf\n",ans);

		return 0;
	}

	double maxi_two = 0;
	//2点消し
	for(int left = 0; left < num_P; left++){

		int right = (left+3)%num_P;

		if(left == right){

			vector<POINT> work_P;
			for(int i = 0; i < N; i++){
				if(i == CH[(left+1)%num_P].p.index || i == CH[(left+2)%num_P].p.index)continue;
				work_P.push_back(POINT(POLY[i].x,POLY[i].Y));
			}

			double tmp_dist = ConvexHull(work_P);
			maxi_two = max(maxi_two,sum-tmp_dist);

			continue;
		}


		vector<Point> ret = calc(left,right,CH[(left+1)%num_P].p.index,CH[(left+2)%num_P].p.index);

		//元々の距離
		double base_dist = calc_dist(CH[left].p,CH[(left+1)%num_P].p)+calc_dist(CH[(left+1)%num_P].p,CH[(left+2)%num_P].p)+
					calc_dist(CH[(left+2)%num_P].p,CH[right].p);


		double new_dist = calc_dist(left,right,ret);

		maxi_two = max(maxi_two,base_dist-new_dist);
	}

	//1点消し
	vector<Info> info;

	for(int left = 0; left < num_P; left++){
		int to_del = (left+1)%num_P;
		int right = (left+2)%num_P;
		double first_len = calc_dist(CH[left].p,CH[to_del].p)+calc_dist(CH[to_del].p,CH[right].p); //元々の長さ

		vector<Point> ret = calc(left,right,CH[to_del].p.index,-1);

		double new_dist = calc_dist(left,right,ret);

		info.push_back(Info(to_del,first_len-new_dist)); //1点消し

		for(int i = 0; i < ret.size(); i++){ //新たに追加された点と合わせて2点消し

			vector<Point> ret2 = calc(left,right,CH[to_del].p.index,ret[i].index);
			double tmp_dist = calc_dist(left,right,ret2);

			maxi_two = max(maxi_two,first_len-tmp_dist);
		}
	}

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

	int num_info = info.size();

	for(int i = 0; i <= min(2,num_info-1); i++){
		for(int k = i+1; k <= min(3,num_info-1); k++){
			if(abs(info[i].del_P-info[k].del_P) == 1 || (info[i].del_P == 0 && info[k].del_P == num_P-1) ||
					(info[i].del_P == num_P-1 && info[k].del_P == 0))continue; //隣接は不可

			maxi_two = max(maxi_two,info[i].minus+info[k].minus);
		}
	}

	printf("%.10lf\n",maxi_two);

	return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 12492
final_rank: 8

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


enum Type{
	Ue,
	Shita,
};

struct Point{
	Point(){

		X = Y = 0;
		x = y = 0;
		index = 0;
	}
	Point(ll arg_X,ll arg_Y){
		X = arg_X;
		Y = arg_Y;
		x = arg_X;
		y = arg_Y;
		index = 0;
	}

	bool operator<(const struct Point& arg) const{
		if(X != arg.X){

			return X < arg.X;
		}else{

			return Y < arg.Y;
		}
	}
	void debug(){

		printf("%lld %lld\n",X,Y);
	}

	ll X,Y;
	double x,y;
	int index;
};

struct POINT{
	POINT(double arg_x,double arg_y){
		x = arg_x;
		y = arg_y;
	}

	POINT(){
		x = y = 0.0;
	}

	POINT operator + (POINT p){ return POINT(x+p.x,y+p.y); }
	POINT operator - (POINT p){ return POINT(x-p.x,y-p.y);}
	POINT operator * (double a){ return POINT(a*x,a*y); }
	POINT operator / (double a){ return POINT(x/a,y/a); }

	double abs(){ return sqrt(norm()); }
	double norm(){ return x*x + y*y; }

	bool operator<(const POINT &p) const{

		if(fabs(x-p.x) > EPS){

			return x < p.x;
		}else{

			return y < p.y;
		}
	}

	bool operator == (const POINT &p) const{
		return fabs(x-p.x) < EPS && fabs(y-p.y) < EPS;
	}


	void debug(){

		printf("(%.3lf,%.3lf)\n",x,y);
	}

	double x,y;
};

typedef Point Vector;
typedef POINT VECTOR;
typedef vector<Point> Polygon;

struct Line{
	Line(){

	}
	Line(Point a,Point b){
		p[0] = a;
		p[1] = b;
	}
	/*void outPut(){

		printf("(%.3lf,%.3lf)-(%.3lf,%.3lf)\n",p[0].x,p[0].y,p[1].x,p[1].y);
	}*/
	Point p[2];
};

struct Info{
	Info(){

		del_P = 0;
		minus = 0;
	}
	Info(int arg_del_P,double arg_minus){
		del_P = arg_del_P;
		minus = arg_minus;
	}
	bool operator<(const struct Info &arg) const{

		return minus > arg.minus;
	}
	int del_P;
	double minus;
};

struct DATA{
	DATA(Point arg_p,Type arg_type){
		p = arg_p;
		type = arg_type;
	}
	DATA(){
		type = Ue;
	}

	Point p;
	Type type;
};

int N;
Polygon POLY;
vector<DATA> CH;

double calc_dist(Point A,Point B){

	return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}

double norm(Vector a){
	return a.x*a.x+a.y*a.y;
}

double abs(Vector a){
	return sqrt(norm(a));
}

double cross(Vector a,Vector b){
    return a.x*b.y-a.y*b.x;
}

double dot(Vector a,Vector b){
    return a.x*b.x + a.y*b.y;
}

//trueなら凸
bool calcGaiseki(Point left,Point base, Point right){
	ll tmp = (left.X-base.X)*(right.Y - base.Y)-(left.Y-base.Y)*(right.X-base.X);
	if(tmp < 0){
		return false;

	}else{
		return true;
	}
}


double norm(VECTOR a){
	return a.x*a.x+a.y*a.y;
}

double abs(VECTOR a){
	return sqrt(norm(a));
}

double cross(VECTOR a,VECTOR b){
    return a.x*b.y-a.y*b.x;
}

double dot(VECTOR a,VECTOR b){
    return a.x*b.x + a.y*b.y;
}

double calc_dist(POINT A,POINT B){

	return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}

static const int COUNTER_CLOCKWISE = 1;
static const int CLOCKWISE = -1;
static const int ONLINE_BACK = 2;
static const int ONLINE_FRONT = -2;
static const int ON_SEGMENT = 0;

/*
 * p0を基準点として、p1から見てp2が
 * 反時計回り側にあれば
 * COUNTER_CLOCKWISE
 * */
int ccw(POINT p0,POINT p1,POINT p2){

	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;
}

double ConvexHull(vector<POINT> V){

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

	vector<POINT> UP,DOWN;

	UP.push_back(V[0]);
	DOWN.push_back(V[0]);

	for(int i = 1; i < V.size(); i++){

		while(UP.size() > 1 && ccw(UP[UP.size()-2],UP[UP.size()-1],V[i]) == COUNTER_CLOCKWISE){

			UP.pop_back();
		}

		while(DOWN.size() > 1 && ccw(DOWN[DOWN.size()-2],DOWN[DOWN.size()-1],V[i]) ==  CLOCKWISE){

			DOWN.pop_back();
		}

		UP.push_back(V[i]);
		DOWN.push_back(V[i]);
	}

	vector<POINT> ret;

	for(int i = 0; i < UP.size(); i++){

		ret.push_back(UP[i]);
	}

	for(int i = DOWN.size()-1; i >= 0; i--){

		ret.push_back(DOWN[i]);
	}

	double tmp_dist = 0;
	for(int i = 0; i < ret.size(); i++){
		tmp_dist += calc_dist(ret[i],ret[(i+1)%ret.size()]);

	}

	return tmp_dist;
}



bool DEBUG = false;


//left～rightの間、del1とdel2を使わずに凸包を求め、その途中の点を返却する関数
//left,rightはCH上のindexを受け取る
vector<Point> calc(int left,int right,int del1,int del2){

	vector<Point> TMP,ret;

	if(CH[left].type == Ue){

		if(CH[right].type == Ue){

			if(DEBUG){

							printf("上上\n");
						}

			if(CH.size() <= 3 || left < right){

				for(int i = CH[left].p.index+1; i <= CH[right].p.index; i++){ //★ちょうどrightで終わるようにする★
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;

					TMP.push_back(POLY[i]);
				}

			}else{ //left > right

				for(int i = CH[left].p.index-1; i >= 0; i--){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;

					TMP.push_back(POLY[i]);
				}
				for(int i = 0; i <= CH[right].p.index; i++){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;

					TMP.push_back(POLY[i]);
				}
			}
		}else{ //CH[right].type == Shita

			for(int i = CH[left].p.index+1; i < N; i++){
				if(POLY[i].index == del1 || POLY[i].index == del2)continue;

				TMP.push_back(POLY[i]);
			}
			for(int i = N-1; i >= CH[right].p.index; i--){
				if(POLY[i].index == del1 || POLY[i].index == del2)continue;

				TMP.push_back(POLY[i]);
			}

		}

	}else{ //CH[left],type == Shita

		if(CH[right].type == Ue){

			for(int i = CH[left].p.index-1; i >= 0; i--){
				if(POLY[i].index == del1 || POLY[i].index == del2)continue;

				TMP.push_back(POLY[i]);
			}
			for(int i = 0; i <= CH[right].p.index; i++){
				if(POLY[i].index == del1 || POLY[i].index == del2)continue;

				TMP.push_back(POLY[i]);
			}
		}else{ //CH[right].type == Shita

			if(DEBUG){

				printf("下下\n");
			}

			if(CH.size() <= 3 || left < right){

				for(int i = CH[left].p.index-1; i >= CH[right].p.index; i--){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;

					TMP.push_back(POLY[i]);
				}
			}else{

				for(int i = CH[left].p.index-1; i >= 0; i--){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;
					TMP.push_back(POLY[i]);
				}
				for(int i = 0; i <= CH[right].p.index; i++){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;
					TMP.push_back(POLY[i]);
				}
			}
		}
	}

	/*if(DEBUG){

		for(int i = 0; i < TMP.size(); i++){

			printf("TMP[%d] ",i);
			TMP[i].debug();
		}
	}*/


	ret.push_back(POLY[CH[(left-1+CH.size())%CH.size()].p.index]);
	ret.push_back(POLY[CH[left].p.index]);


	for(int i = 0; i < TMP.size(); i++){
		if(TMP[i].index == ret.back().index)continue;
		while(ret.size() > 1 && calcGaiseki(ret[ret.size()-2],ret[ret.size()-1],TMP[i]) == false)ret.pop_back();
		ret.push_back(TMP[i]);
	}

	/*if(DEBUG){

		printf("\nret\n");
		for(int i = 0; i < ret.size(); i++){

			printf("ret[%d] ",i);
			ret[i].debug();
		}
	}*/

	vector<Point> work;

	for(int i = 2; i < ret.size()-1; i++){

		work.push_back(ret[i]);
	}

	return work;
}

double calc_dist(int left,int right,vector<Point> vec){

	double ret = 0;
	if(vec.size() > 0){

		ret += calc_dist(CH[left].p,vec[0])+calc_dist(CH[right].p,vec[vec.size()-1]);
		for(int i = 1; i < vec.size(); i++){

			ret += calc_dist(vec[i],vec[i-1]);
		}
	}else{

		ret = calc_dist(CH[left].p,CH[right].p);
	}

	return ret;
}



int main(){


	scanf("%d",&N);

	ll X,Y;
	for(int i = 0; i < N; i++){

		scanf("%lld %lld",&X,&Y);
		POLY.push_back(Point(X,Y));
	}

	sort(POLY.begin(),POLY.end());
	for(int i = 0; i < N; i++){

		POLY[i].index = i; //sortした中での何番目か
		if(DEBUG){

			printf("index:%d \n",i);
			POLY[i].debug();
		}
	}

	Polygon V = POLY;

	vector<Point> UP,DOWN;

	UP.push_back(V[0]);
	UP.push_back(V[1]);

	DOWN.push_back(V[V.size()-1]);
	DOWN.push_back(V[V.size()-2]);

	for(int i = 2; i < V.size(); i++){
		while(UP.size() > 1 && calcGaiseki(UP[UP.size()-2],UP[UP.size()-1],V[i]) == false)UP.pop_back();
		UP.push_back(V[i]);
	}

	for(int i = V.size()-3; i >= 0; i--){
		while(DOWN.size() > 1 && calcGaiseki(DOWN[DOWN.size()-2],DOWN[DOWN.size()-1],V[i]) == false){
			DOWN.pop_back();
		}
		DOWN.push_back(V[i]);
	}

	if(DEBUG){
		printf("\n凸包\n");
		printf("%d\n",UP.size()+DOWN.size()-2);
		for(int i = DOWN.size()-1; i >= 1;i--)printf("%.0lf %.0lf\n",DOWN[i].x,DOWN[i].y);
		for(int i = UP.size()-1; i >= 1 ;i--)printf("%.0lf %.0lf\n",UP[i].x,UP[i].y);
	}

	for(int i =0; i < UP.size()-1;i++){

		CH.push_back(DATA(UP[i],Ue));
	}
	for(int i = 0; i < DOWN.size()-1 ;i++){

		CH.push_back(DATA(DOWN[i],Shita));
	}

	if(DEBUG){
		//時計回りにしておく
		for(int i = 0; i < CH.size(); i++){

			printf("CH[%d] index:%d\n",i,CH[i].p.index);
			CH[i].p.debug();
		}
	}

	int num_P = CH.size();

	double sum = 0;
	for(int i = 0; i < CH.size(); i++){

		sum += calc_dist(CH[i].p,CH[(i+1)%num_P].p);
	}

	if(N <= 500){

		double ans = 0;

		for(int i = 0; i < N; i++){
			for(int k = 0; k < N; k++){
				if(k == i)continue;
				vector<POINT> P;
				for(int a = 0; a < N; a++){
					if(a == i || a == k)continue;

					P.push_back(POINT(POLY[a].x,POLY[a].y));
				}
				ans = max(ans,sum-ConvexHull(P));
			}
		}

		printf("%.10lf\n",ans);

		return 0;
	}

	if(DEBUG){
		printf("sum:%.3lf\n",sum);
		vector<POINT> deb_P;
		for(int i = 0; i < N; i++){

			deb_P.push_back(POINT(POLY[i].x,POLY[i].y));
		}
		double hull_S = ConvexHull(deb_P);
		printf("hull_S:%.3lf\n",hull_S);
	}

	/*printf("num_P:%d\n",num_P);
	return 0;*/

	double maxi_two = 0;
	//2点消し
	for(int left = 0; left < num_P; left++){

		int right = (left+3)%num_P;

		if(left == right){

			vector<POINT> work_P;
			for(int i = 0; i < N; i++){
				if(i == CH[(left+1)%num_P].p.index || i == CH[(left+2)%num_P].p.index)continue;
				work_P.push_back(POINT(POLY[i].x,POLY[i].Y));
			}

			double tmp_dist = ConvexHull(work_P);
			maxi_two = max(maxi_two,sum-tmp_dist);

			continue;
		}


		vector<Point> ret = calc(left,right,CH[(left+1)%num_P].p.index,CH[(left+2)%num_P].p.index);

		if(DEBUG){
		printf("ret.size():%lld\n",ret.size());
		for(int i = 0; i < ret.size(); i++){

			printf("ret[%d] ",i);
			ret[i].debug();
		}
		}

		//元々の距離
		double base_dist = calc_dist(CH[left].p,CH[(left+1)%num_P].p)+calc_dist(CH[(left+1)%num_P].p,CH[(left+2)%num_P].p)+
					calc_dist(CH[(left+2)%num_P].p,CH[right].p);


		double new_dist = calc_dist(left,right,ret);
		if(DEBUG){
			printf("base_dist:%.3lf new_dist:%.3lf\n",base_dist,new_dist);
			printf("left:%d right:%d base_dist-new_dist:%.3lf\n",left,right,base_dist-new_dist);
		}

		maxi_two = max(maxi_two,base_dist-new_dist);
	}

	if(DEBUG){
	printf("2点消し後 maxi_two:%.10lf\n",maxi_two);
	}

	//1点消し
	vector<Info> info;

	for(int left = 0; left < num_P; left++){
		int to_del = (left+1)%num_P;
		int right = (left+2)%num_P;
		double first_len = calc_dist(CH[left].p,CH[to_del].p)+calc_dist(CH[to_del].p,CH[right].p); //元々の長さ

		vector<Point> ret = calc(left,right,CH[to_del].p.index,-1);

		if(DEBUG){
			if(to_del == 0){
				printf("first_len:%.3lf ret.size():%lld\n",first_len,ret.size());
				for(int i = 0; i < ret.size(); i++){

					printf("ret[%d];%d\n",i,ret[i].index);
				}
			}
		}

		double new_dist = calc_dist(left,right,ret);

		info.push_back(Info(to_del,first_len-new_dist)); //1点消し

		for(int i = 0; i < ret.size(); i++){ //新たに追加された点と合わせて2点消し

			vector<Point> ret2 = calc(left,right,CH[to_del].p.index,ret[i].index);
			double tmp_dist = calc_dist(left,right,ret2);

			maxi_two = max(maxi_two,first_len-tmp_dist);
		}
	}

	if(DEBUG){
	printf("1点消し後 maxi_two:%.10lf\n",maxi_two);
	}

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



	if(DEBUG){
		printf("info.size():%lld\n",info.size());
		for(int i = 0; i <= 3; i++){

			printf("%.3lf del_P:%d\n",info[i].minus,info[i].del_P);
		}
	}

	int num_info = info.size();

	for(int i = 0; i <= min(2,num_info-1); i++){
		for(int k = i+1; k <= min(3,num_info-1); k++){
			if(abs(info[i].del_P-info[k].del_P) == 1 || (info[i].del_P == 0 && info[k].del_P == num_P-1) ||
					(info[i].del_P == num_P-1 && info[k].del_P == 0))continue; //隣接は不可

			maxi_two = max(maxi_two,info[i].minus+info[k].minus);
		}
	}

	printf("%.10lf\n",maxi_two);

	return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 12492
final_rank: 8

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


enum Type{
	Ue,
	Shita,
};

struct Point{
	Point(){

		X = Y = 0;
		x = y = 0;
		index = 0;
	}
	Point(ll arg_X,ll arg_Y){
		X = arg_X;
		Y = arg_Y;
		x = arg_X;
		y = arg_Y;
		index = 0;
	}

	bool operator<(const struct Point& arg) const{
		if(X != arg.X){

			return X < arg.X;
		}else{

			return Y < arg.Y;
		}
	}
	void debug(){

		printf("%lld %lld\n",X,Y);
	}

	ll X,Y;
	double x,y;
	int index;
};

struct POINT{
	POINT(double arg_x,double arg_y){
		x = arg_x;
		y = arg_y;
	}

	POINT(){
		x = y = 0.0;
	}

	POINT operator + (POINT p){ return POINT(x+p.x,y+p.y); }
	POINT operator - (POINT p){ return POINT(x-p.x,y-p.y);}
	POINT operator * (double a){ return POINT(a*x,a*y); }
	POINT operator / (double a){ return POINT(x/a,y/a); }

	double abs(){ return sqrt(norm()); }
	double norm(){ return x*x + y*y; }

	bool operator<(const POINT &p) const{

		if(fabs(x-p.x) > EPS){

			return x < p.x;
		}else{

			return y < p.y;
		}
	}

	bool operator == (const POINT &p) const{
		return fabs(x-p.x) < EPS && fabs(y-p.y) < EPS;
	}


	void debug(){

		printf("(%.3lf,%.3lf)\n",x,y);
	}

	double x,y;
};

typedef Point Vector;
typedef POINT VECTOR;
typedef vector<Point> Polygon;

struct Line{
	Line(){

	}
	Line(Point a,Point b){
		p[0] = a;
		p[1] = b;
	}
	/*void outPut(){

		printf("(%.3lf,%.3lf)-(%.3lf,%.3lf)\n",p[0].x,p[0].y,p[1].x,p[1].y);
	}*/
	Point p[2];
};

struct Info{
	Info(){

		del_P = 0;
		minus = 0;
	}
	Info(int arg_del_P,double arg_minus){
		del_P = arg_del_P;
		minus = arg_minus;
	}
	bool operator<(const struct Info &arg) const{

		return minus > arg.minus;
	}
	int del_P;
	double minus;
};

struct DATA{
	DATA(Point arg_p,Type arg_type){
		p = arg_p;
		type = arg_type;
	}
	DATA(){
		type = Ue;
	}

	Point p;
	Type type;
};

int N;
Polygon POLY;
vector<DATA> CH;

double calc_dist(Point A,Point B){

	return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}

double norm(Vector a){
	return a.x*a.x+a.y*a.y;
}

double abs(Vector a){
	return sqrt(norm(a));
}

double cross(Vector a,Vector b){
    return a.x*b.y-a.y*b.x;
}

double dot(Vector a,Vector b){
    return a.x*b.x + a.y*b.y;
}

//trueなら凸
bool calcGaiseki(Point left,Point base, Point right){
	ll tmp = (left.X-base.X)*(right.Y - base.Y)-(left.Y-base.Y)*(right.X-base.X);
	if(tmp < 0){
		return false;

	}else{
		return true;
	}
}


double norm(VECTOR a){
	return a.x*a.x+a.y*a.y;
}

double abs(VECTOR a){
	return sqrt(norm(a));
}

double cross(VECTOR a,VECTOR b){
    return a.x*b.y-a.y*b.x;
}

double dot(VECTOR a,VECTOR b){
    return a.x*b.x + a.y*b.y;
}

double calc_dist(POINT A,POINT B){

	return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}

static const int COUNTER_CLOCKWISE = 1;
static const int CLOCKWISE = -1;
static const int ONLINE_BACK = 2;
static const int ONLINE_FRONT = -2;
static const int ON_SEGMENT = 0;

/*
 * p0を基準点として、p1から見てp2が
 * 反時計回り側にあれば
 * COUNTER_CLOCKWISE
 * */
int ccw(POINT p0,POINT p1,POINT p2){

	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;
}

double ConvexHull(vector<POINT> V){

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

	vector<POINT> UP,DOWN;

	UP.push_back(V[0]);
	DOWN.push_back(V[0]);

	for(int i = 1; i < V.size(); i++){

		while(UP.size() > 1 && ccw(UP[UP.size()-2],UP[UP.size()-1],V[i]) == COUNTER_CLOCKWISE){

			UP.pop_back();
		}

		while(DOWN.size() > 1 && ccw(DOWN[DOWN.size()-2],DOWN[DOWN.size()-1],V[i]) ==  CLOCKWISE){

			DOWN.pop_back();
		}

		UP.push_back(V[i]);
		DOWN.push_back(V[i]);
	}

	vector<POINT> ret;

	for(int i = 0; i < UP.size(); i++){

		ret.push_back(UP[i]);
	}

	for(int i = DOWN.size()-1; i >= 0; i--){

		ret.push_back(DOWN[i]);
	}

	double tmp_dist = 0;
	for(int i = 0; i < ret.size(); i++){
		tmp_dist += calc_dist(ret[i],ret[(i+1)%ret.size()]);

	}

	return tmp_dist;
}



bool DEBUG = false;


//left～rightの間、del1とdel2を使わずに凸包を求め、その途中の点を返却する関数
//left,rightはCH上のindexを受け取る
vector<Point> calc(int left,int right,int del1,int del2){

	vector<Point> TMP,ret;

	if(CH[left].type == Ue){

		if(CH[right].type == Ue){

			if(left < right){

				for(int i = CH[left].p.index+1; i <= CH[right].p.index; i++){ //★ちょうどrightで終わるようにする★
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;

					TMP.push_back(POLY[i]);
				}

			}else{ //left > right

				for(int i = CH[left].p.index-1; i >= 0; i--){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;

					TMP.push_back(POLY[i]);
				}
				for(int i = 0; i <= CH[right].p.index; i++){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;

					TMP.push_back(POLY[i]);
				}
			}
		}else{ //CH[right].type == Shita

			for(int i = CH[left].p.index+1; i < N; i++){
				if(POLY[i].index == del1 || POLY[i].index == del2)continue;

				TMP.push_back(POLY[i]);
			}
			for(int i = N-1; i >= CH[right].p.index; i--){
				if(POLY[i].index == del1 || POLY[i].index == del2)continue;

				TMP.push_back(POLY[i]);
			}

		}

	}else{ //CH[left],type == Shita

		if(CH[right].type == Ue){

			for(int i = CH[left].p.index-1; i >= 0; i--){
				if(POLY[i].index == del1 || POLY[i].index == del2)continue;

				TMP.push_back(POLY[i]);
			}
			for(int i = 0; i <= CH[right].p.index; i++){
				if(POLY[i].index == del1 || POLY[i].index == del2)continue;

				TMP.push_back(POLY[i]);
			}
		}else{ //CH[right].type == Shita

			if(left < right){

				for(int i = CH[left].p.index-1; i >= CH[right].p.index; i--){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;

					TMP.push_back(POLY[i]);
				}
			}else{

				for(int i = CH[left].p.index+1; i < N; i++){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;
					TMP.push_back(POLY[i]);
				}
				for(int i = N-1; i >= 0; i--){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;
					TMP.push_back(POLY[i]);
				}
				for(int i = 0; i <= CH[right].p.index; i++){
					if(POLY[i].index == del1 || POLY[i].index == del2)continue;
					TMP.push_back(POLY[i]);
				}
			}
		}
	}

	/*if(DEBUG){

		for(int i = 0; i < TMP.size(); i++){

			printf("TMP[%d] ",i);
			TMP[i].debug();
		}
	}*/


	ret.push_back(POLY[CH[(left-1+CH.size())%CH.size()].p.index]);
	ret.push_back(POLY[CH[left].p.index]);


	for(int i = 0; i < TMP.size(); i++){
		if(TMP[i].index == ret.back().index)continue;
		while(ret.size() > 1 && calcGaiseki(ret[ret.size()-2],ret[ret.size()-1],TMP[i]) == false)ret.pop_back();
		ret.push_back(TMP[i]);
	}

	/*if(DEBUG){

		printf("\nret\n");
		for(int i = 0; i < ret.size(); i++){

			printf("ret[%d] ",i);
			ret[i].debug();
		}
	}*/

	vector<Point> work;

	for(int i = 2; i < ret.size()-1; i++){

		work.push_back(ret[i]);
	}

	return work;
}

double calc_dist(int left,int right,vector<Point> vec){

	double ret = 0;
	if(vec.size() > 0){

		ret += calc_dist(CH[left].p,vec[0])+calc_dist(CH[right].p,vec[vec.size()-1]);
		for(int i = 1; i < vec.size(); i++){

			ret += calc_dist(vec[i],vec[i-1]);
		}
	}else{

		ret = calc_dist(CH[left].p,CH[right].p);
	}

	return ret;
}



int main(){


	scanf("%d",&N);

	ll X,Y;
	for(int i = 0; i < N; i++){

		scanf("%lld %lld",&X,&Y);
		POLY.push_back(Point(X,Y));
	}

	sort(POLY.begin(),POLY.end());
	for(int i = 0; i < N; i++){

		POLY[i].index = i; //sortした中での何番目か
		if(DEBUG){

			printf("index:%d \n",i);
			POLY[i].debug();
		}
	}

	Polygon V = POLY;

	vector<Point> UP,DOWN;

	UP.push_back(V[0]);
	UP.push_back(V[1]);

	DOWN.push_back(V[V.size()-1]);
	DOWN.push_back(V[V.size()-2]);

	for(int i = 2; i < V.size(); i++){
		while(UP.size() > 1 && calcGaiseki(UP[UP.size()-2],UP[UP.size()-1],V[i]) == false)UP.pop_back();
		UP.push_back(V[i]);
	}

	for(int i = V.size()-3; i >= 0; i--){
		while(DOWN.size() > 1 && calcGaiseki(DOWN[DOWN.size()-2],DOWN[DOWN.size()-1],V[i]) == false){
			DOWN.pop_back();
		}
		DOWN.push_back(V[i]);
	}

	if(DEBUG){
		printf("\n凸包\n");
		printf("%d\n",UP.size()+DOWN.size()-2);
		for(int i = DOWN.size()-1; i >= 1;i--)printf("%.0lf %.0lf\n",DOWN[i].x,DOWN[i].y);
		for(int i = UP.size()-1; i >= 1 ;i--)printf("%.0lf %.0lf\n",UP[i].x,UP[i].y);
	}

	for(int i =0; i < UP.size()-1;i++){

		CH.push_back(DATA(UP[i],Ue));
	}
	for(int i = 0; i < DOWN.size()-1 ;i++){

		CH.push_back(DATA(DOWN[i],Shita));
	}

	if(DEBUG){
		//時計回りにしておく
		for(int i = 0; i < CH.size(); i++){

			printf("CH[%d] index:%d\n",i,CH[i].p.index);
			CH[i].p.debug();
		}
	}

	int num_P = CH.size();

	double sum = 0;
	for(int i = 0; i < CH.size(); i++){

		sum += calc_dist(CH[i].p,CH[(i+1)%num_P].p);
	}

	if(DEBUG){
		printf("sum:%.3lf\n",sum);
		vector<POINT> deb_P;
		for(int i = 0; i < N; i++){

			deb_P.push_back(POINT(POLY[i].x,POLY[i].y));
		}
		double hull_S = ConvexHull(deb_P);
		printf("hull_S:%.3lf\n",hull_S);
	}

	/*printf("num_P:%d\n",num_P);
	return 0;*/


	double maxi_two = 0;
	//2点消し
	for(int left = 0; left < num_P; left++){

		int right = (left+3)%num_P;

		if(left == right){

			vector<POINT> work_P;
			for(int i = 0; i < N; i++){
				if(i == CH[(left+1)%num_P].p.index || i == CH[(left+2)%num_P].p.index)continue;
				work_P.push_back(POINT(POLY[i].x,POLY[i].Y));
			}

			double tmp_dist = ConvexHull(work_P);
			maxi_two = max(maxi_two,sum-tmp_dist);

			continue;
		}


		vector<Point> ret = calc(left,right,CH[(left+1)%num_P].p.index,CH[(left+2)%num_P].p.index);

		//元々の距離
		double base_dist = calc_dist(CH[left].p,CH[(left+1)%num_P].p)+calc_dist(CH[(left+1)%num_P].p,CH[(left+2)%num_P].p)+
					calc_dist(CH[(left+2)%num_P].p,CH[right].p);


		double new_dist = calc_dist(left,right,ret);
		if(DEBUG){

			printf("left:%d right:%d base_dist-new_dist:%.3lf\n",left,right,base_dist-new_dist);
		}

		maxi_two = max(maxi_two,base_dist-new_dist);
	}

	if(DEBUG){
	printf("2点消し後 maxi_two:%.10lf\n",maxi_two);
	}

	//1点消し
	vector<Info> info;

	for(int left = 0; left < num_P; left++){
		int to_del = (left+1)%num_P;
		int right = (left+2)%num_P;
		double first_len = calc_dist(CH[left].p,CH[to_del].p)+calc_dist(CH[to_del].p,CH[right].p); //元々の長さ

		vector<Point> ret = calc(left,right,CH[to_del].p.index,-1);

		if(DEBUG){
			if(to_del == 0){
				printf("first_len:%.3lf ret.size():%lld\n",first_len,ret.size());
				for(int i = 0; i < ret.size(); i++){

					printf("ret[%d];%d\n",i,ret[i].index);
				}
			}
		}

		double new_dist = calc_dist(left,right,ret);

		info.push_back(Info(to_del,first_len-new_dist)); //1点消し

		for(int i = 0; i < ret.size(); i++){ //新たに追加された点と合わせて2点消し

			vector<Point> ret2 = calc(left,right,CH[to_del].p.index,ret[i].index);
			double tmp_dist = calc_dist(left,right,ret2);

			maxi_two = max(maxi_two,first_len-tmp_dist);
		}
	}

	if(DEBUG){
	printf("1点消し後 maxi_two:%.10lf\n",maxi_two);
	}

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



	if(DEBUG){
		printf("info.size():%lld\n",info.size());
		for(int i = 0; i <= 3; i++){

			printf("%.3lf del_P:%d\n",info[i].minus,info[i].del_P);
		}
	}

	int num_info = info.size();

	for(int i = 0; i <= min(2,num_info-1); i++){
		for(int k = i+1; k <= min(3,num_info-1); k++){
			if(abs(info[i].del_P-info[k].del_P) == 1 || (info[i].del_P == 0 && info[k].del_P == num_P-1) ||
					(info[i].del_P == num_P-1 && info[k].del_P == 0))continue; //隣接は不可

			maxi_two = max(maxi_two,info[i].minus+info[k].minus);
		}
	}

	printf("%.10lf\n",maxi_two);

	return 0;
}
accuracy: 0.02631578947368421
time_ms: 10
memory_kb: 3528
final_rank: 10

code_snippet: #include <iostream>
#include <vector>
#include <array>
#include <list>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <tuple>
#include <bitset>
#include <memory>
#include <cmath>
#include <algorithm>
#include <functional>
#include <iomanip>
#include <numeric>
#include <climits>
#include <cfloat>
#include <cassert>
#include <random>

struct Point {
	long long int x, y;
	double distance(const Point& that) const;
	bool operator==(const Point& that) const;
	bool operator!=(const Point& that) const;
};
struct Vector {
	long long int x, y;
	double norm() const;
	long long int cross(const Vector& that) const;
};
Vector operator-(const Point& to, const Point& from) {
	return Vector{ to.x - from.x, to.y - from.y };
}
void remove_convex(const std::vector<Point>& points, std::vector<int>& result, const int from, const int to, const int remove1, const int remove2 = -1) {
	result.clear();
	for (auto i = from; i != to; i = (i + 1) % points.size()) if (points[i] != points[remove1] && (remove2 == -1 || points[i] != points[remove2])) {
		while (result.size() >= 2) {
			if ((points[result[result.size() - 2]] - points[result[result.size() - 1]]).cross(points[i] - points[result[result.size() - 1]]) < 0) {
				result.pop_back();
			}
			else {
				break;
			}
		}
		if (result.empty() || points[result.back()] != points[i]) result.push_back(i);
	}
	while (result.size() >= 2 && (points[result[result.size() - 2]] - points[result[result.size() - 1]]).cross(points[to] - points[result[result.size() - 1]]) < 0) {
		result.pop_back();
	}
	if (result.empty() || points[result.back()] != points[to]) result.push_back(to);
}
void map(std::vector<Point>& map, const std::vector<int>& index, const std::vector<Point>& points) {
	map.clear();
	for (const auto i : index) map.push_back(points[i]);
}
int main() {
	int n; std::cin >> n;
	std::vector<Point> points(n); for (auto& p : points) std::cin >> p.x >> p.y;
	std::sort(points.begin(), points.end(), [](const Point& a, const Point& b) {return a.x == b.x ? a.y < b.y : a.x < b.x; });
	for (int i = points.size() - 2; i > 0; --i) points.push_back(points[i]);
	std::vector<int> convex;
	for (auto i = 0; i < points.size(); ++i) {
		while (convex.size() >= 2) {
			if ((points[convex[convex.size() - 2]] - points[convex[convex.size() - 1]]).cross(points[i] - points[convex[convex.size() - 1]]) < 0) {
				convex.pop_back();
			}
			else {
				break;
			}
		}
		convex.push_back(i);
	}
	while ((points[convex[convex.size() - 2]] - points[convex[convex.size() - 1]]).cross(points[0] - points[convex[convex.size() - 1]]) < 0) {
		convex.pop_back();
	}
	std::vector<int> index, second;
	std::vector<Point> index_map, second_map;
	std::vector<double> reduce_single(convex.size(), 0);
	double max_reduce_double = 0, max_except_zero = 0;
	for (auto i = 0; i < convex.size(); ++i) {
		auto prev = convex[(convex.size() + i - 1) % convex.size()];
		auto current = convex[i];
		auto next = convex[(i + 1) % convex.size()];
		reduce_single[i] = points[prev].distance(points[current]) + points[current].distance(points[next]);
		remove_convex(points, index, prev, next, current);
		for (auto j = 1; j < index.size(); ++j) {
			reduce_single[i] -= points[index[j]].distance(points[index[j - 1]]);
		}
		map(index_map, index, points);
		index.push_back(convex[(i + 2) % convex.size()]);
		for (auto j = 1; j + 1 < index.size(); ++j) {
			remove_convex(points, second, index[j - 1], index[j + 1], current, index[j]);
			double reduce = points[index[j - 1]].distance(points[index[j]]) + points[index[j]].distance(points[index[j + 1]]);
			for (auto k = 1; k < second.size(); ++k) {
				reduce -= points[second[k]].distance(points[second[k - 1]]);
			}
			map(second_map, second, points);
			max_reduce_double = std::max(max_reduce_double, reduce + reduce_single[i]);
		}
	}
	for (auto i = 3; i + 1 < convex.size(); ++i) {
		max_except_zero = std::max(max_except_zero, reduce_single[i - 2]);
		max_reduce_double = std::max({ max_except_zero + reduce_single[i], max_reduce_double, reduce_single[0] + reduce_single[i] });
	}
	max_reduce_double = std::max(max_reduce_double, reduce_single.back() + max_except_zero);
	std::cout << std::setprecision(15) << std::fixed << max_reduce_double << '\n';
}

double Point::distance(const Point& that) const
{
	return (*this - that).norm();
}

bool Point::operator==(const Point& that) const
{
	return x == that.x && y == that.y;
}

bool Point::operator!=(const Point& that) const
{
	return !(*this == that);
}

double Vector::norm() const
{
	return std::sqrt(x * x + y * y);
}

long long int Vector::cross(const Vector& that) const
{
	return x * that.y - y * that.x;
}
accuracy: 1
time_ms: 70
memory_kb: 6112
final_rank: 4

code_snippet: #include <bits/stdc++.h>

#define ALL(x) (x).begin(), (x).end()

using LL = long long;

const int max_N = (int) 1e5 + 21;

struct P { 
	int x, y;
	
	explicit P(int x = 0, int y = 0) : x(x), y(y) {}
	
	inline P add(const P &p) const { return P(x + p.x, y + p.y); }
	
	inline P sub(const P &p) const { return P(x - p.x, y - p.y); }
	
	inline LL dot(const P &p) const { return 1ll * x * p.x + 1ll * y * p.y; }
	
	inline LL det(const P &p) const { return 1ll * x * p.y - 1ll * y * p.x; }
	
	inline LL abs2() { return dot(*this); }
	
	inline double abs() { return std::sqrt(abs2()); }
	
	bool operator<(const P &p) const { return x != p.x ? x < p.x : y < p.y; }
	
	bool operator==(const P &p) const { return x == p.x && y == p.y; }
} p[max_N], o;

bool origin[max_N], added[max_N]; 

int n, st[max_N], top, Q[max_N], tot; 

double ans;

std::vector<int> vec[3];

void initConvex() {
	std::sort(p + 1, p + 1 + n); 
	top = 0;
	auto check = [&](int i) -> bool {
		return p[i].sub(p[st[top - 1]]).det(p[st[top]].sub(p[st[top - 1]])) >= 0;
	};
	for (int i = 1; i <= n; ++i) {
		while (top > 1 && check(i)) --top; 
		st[++top] = i;
	}
	o = p[st[1]].add(p[st[top]]); 
	o.x >>= 1, o.y >>= 1; 
	for (int i = n - 1, tmp = top; i; --i) { 
		while (top > tmp && check(i)) --top; 
		st[++top] = i;
	}
	if (top > 1) --top;
	for (int i = 1; i <= top; ++i) origin[st[i]] = true;
}

std::priority_queue<std::pair<double, int>>pq;

void solve(int id, double delta, std::vector<int> &v, bool tp) { 
	static int st[2][max_N], Q[max_N]; 
	int top = 0;
	auto check = [&](int i) -> bool {
		return p[i].sub(p[st[tp][top - 1]]).det(p[st[tp][top]].sub(p[st[tp][top - 1]])) >= 0;
	};
	for (auto i : v) {
		while (top > 1 && check(i)) --top; 
		st[tp][++top] = i;
	}
	for (int i = 1; i < top; ++i) {
		delta += p[st[tp][i]].sub(p[st[tp][i + 1]]).abs();
	}
	ans = std::max(ans, -delta); 
	if (tp) {
		pq.push({delta, id});
		// printfC’X.lGfNn", delta); 
		if (pq.size() > 4) pq.pop();
		for (int i = 1; i <= top; ++i) added[st[tp][i]] = true; 
		int tot = 0;
		for (auto i = 0; i < v.size(); ++i) { 
			int x = v[i]; 
			if (added[x]) {
				Q[++tot] = i;
				// printf("added(96d, %d)\n", p[x].x, p[x].y);
			}
		}
		for (int i = 2; i < tot; ++i) { 
			std::vector<int> vv;
			for (int j = Q[i - 1]; j <= Q[i + 1]; ++j) { 
				if (j == Q[i]) continue; 
				vv.push_back(v[j]);
			}
			double delta1 = delta;
			delta1 -= p[v[Q[i]]].sub(p[v[Q[i - 1]]]).abs(); 
			delta1 -= p[v[Q[i]]].sub(p[v[Q[i + 1]]]).abs(); 
			solve(0, delta1, vv, false);
		}
		for (int i = 1; i <= top; ++i) added[st[tp][i]] = false;
	}
}

void pickUp(int id, std::vector<int> &v) { 
	if (id == tot) {
		for (auto i = Q[tot] + 1; i < vec[2].size(); ++i) { 
			v.push_back(vec[2][i]);
		}
		for (int i = 1; i < Q[1]; ++i) { 
			v.push_back(vec[2][i]);
		}
	} else {
		for (int i = Q[id] + 1; i < Q[id + 1]; ++i) { 
			v.push_back(vec[2][i]);
		}
	}
}

bool adjacent(int i, int j) { 
	if (i == j) return true; 
	if (i > j) std::swap(i, j); 
	return i + 1 == j || (i == 1 && j == tot);
}

void solve() {
	for (int i = 1; i <= n; ++i) {
		P v = p[i].sub(o);
		int d = v.y > 0 || (!v.y && v.x > 0); 
		vec[d].push_back(i);
	}
	for (int d = 0; d < 2; ++d) {
		std::sort(ALL(vec[d]), [&](int x, int y) { 
			return p[x].sub(o).det(p[y].sub(o)) > 0;
		});
		for (auto x : vec[d]) vec[2].push_back(x);
	}
	for (auto i = 0; i < vec[2].size(); ++i) { 
		int x = vec[2][i]; 
		if (origin[x]) Q[++tot] = i;
		// printf('’sort(%d, 36d)\nn, p[x].x, p[x].y);
	}
	for (int i = 1; i <= tot; ++i) { 
		int prev = i == 1 ? tot : i - 1; 
		int next = i == tot ? 1 : i + 1; 
		std::vector<int> v; 
		v.push_back(vec[2][Q[prev]]); 
		pickUp(prev, v), pickUp(i, v); 
		v.push_back(vec[2][Q[next]]); 
		double delta = 0;
		delta -= p[vec[2][Q[i]]].sub(p[vec[2][Q[prev]]]).abs(); 
		delta -= p[vec[2][Q[i]]].sub(p[vec[2][Q[next]]]).abs();
		// printf("delta(96d, %d) = ", p[vec[2][Q[i]]j.x, p[vec[2][Q[i]]].y); 
		solve(i, delta, v, true);
	}

	std::vector<std::pair<double, int>> best4; 
	while (!pq.empty()) {
		best4.push_back(pq.top()); 
		pq.pop();
	}
	for (auto a : best4)
		for (auto b : best4) {
			if (adjacent(a.second, b.second)) continue;
			// printf("nonadjacent(%d, %d) = %.10f, %.10f\n”, a.second, b.second, a.first, b.first); 
			ans = std::max(ans, -(a.first + b.first));
		}
		
	for (int i = 1; i <= tot; ++i) { 
		int prev = i == 1 ? tot : i - 1;
		int next = i == tot ? 1 : i + 1;
		int nnext = next == tot ? 1 : next + 1;
		std::vector<int> v; 
		v.push_back(vec[2][Q[prev]]); 
		pickUp(prev, v), pickUp(i, v), pickUp(next, v); 
		v.push_back(vec[2][Q[nnext]]); 
		double delta = 0;
		delta -= p[vec[2][Q[i]]].sub(p[vec[2][Q[prev]]]).abs(); 
		delta -= p[vec[2][Q[i]]].sub(p[vec[2][Q[next]]]).abs(); 
		delta -= p[vec[2][Q[nnext]]].sub(p[vec[2][Q[next]]]).abs(); 
		solve(0, delta, v, false);
	}
}


int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		scanf("%d%d", &p[i].x, &p[i].y);
	}
	initConvex();
	solve();
	printf("%.10f\n", ans); 
	return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 5028
final_rank: 1

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

using DB = double;
struct PT {
	int x, y;
	PT (int x = 0, int y = 0) : x(x), y(y) {}
	void in() { scanf("%d%d", &x, &y); }
	bool operator <(const PT &pts) const { return make_pair(x, y) < make_pair(pts.x, pts.y); }
	bool operator ==(const PT &pts) const { return make_pair(x, y) == make_pair(pts.x, pts.y); }
};

DB sqr(int x) { return (DB)x * x; }

DB dis(PT p1, PT p2) {
	return sqrt(sqr(p1.x - p2.x) + sqr(p1.y - p2.y));
}

long long vect(PT p, PT p1, PT p2) {
	return 1LL * (p1.x - p.x) * (p2.y - p.y) - 1LL * (p1.y - p.y) * (p2.x - p.x);
}

long long scal(PT p, PT p1, PT p2) {
	return 1LL * (p1.x - p.x) * (p2.x - p.x) + 1LL * (p1.y - p.y) * (p2.y - p.y);
}

bool check(PT p1, PT p2, PT p3) {
	if (vect(p1, p2, p3) < 0) return 1;
	if (!vect(p1, p2, p3)) return scal(p2, p1, p3) <= 0;
	return 0;
}

vector<PT> pts, cvx;

void convex() {
	sort(pts.begin(), pts.end());
	pts.erase(unique(pts.begin(), pts.end()), pts.end());
	if (pts.size() == 1) {
		cvx.push_back(pts[0]);
		return;
	}
	for (int times = 0; times < 2; times++) {
		for (auto t : pts) {
			while (cvx.size() > 1 && check(cvx[cvx.size() - 2], cvx.back(), t)) cvx.pop_back();
			cvx.push_back(t);
		}
		reverse(pts.begin(), pts.end());
	}
	cvx.pop_back();
}

int getId(PT p) {
	return lower_bound(pts.begin(), pts.end(), p) - pts.begin();
}

vector<PT> getConvex(PT lft, PT cur, PT rht, PT nV) {
	vector<PT> nC;
	int px = getId(lft), cx = getId(cur), rx = getId(rht);
	int now = px;
	while (now != cx) {
		if (!(pts[now] == nV)) {
			while (nC.size() > 1 && check(nC[nC.size() - 2], nC.back(), pts[now])) nC.pop_back();
			nC.push_back(pts[now]);
		}
		if (now < cx) ++now;
		else --now;
	}
	while (now != rx) {
		if (now < rx) ++now;
		else --now;
		if (!(pts[now] == nV)) {
			while (nC.size() > 1 && check(nC[nC.size() - 2], nC.back(), pts[now])) nC.pop_back();
			nC.push_back(pts[now]);
		}
	}
	return nC;
}

vector<DB> profit;
multiset<DB> PM;
DB ans;

void process() {
	int sz = cvx.size();
	for (int i = 0; i < cvx.size(); i++) {
		PT lft = cvx[(i - 1 + sz) % sz], cur = cvx[i], rht = cvx[(i + 1) % sz];
		DB lose = dis(lft, cur) + dis(cur, rht);
		vector<PT> nC = getConvex(lft, cur, rht, PT(1e8, 1e8));
		DB get = 0;
		for (int j = 1; j < nC.size(); j++) get += dis(nC[j - 1], nC[j]);
		profit.push_back(lose - get);
		PM.insert(lose - get);
		nC.push_back(cvx[(i + 2) % sz]);
		for (int j = 1; j < (int)nC.size() - 1; j++) {
			PT _lft = nC[j - 1], _cur = nC[j], _nxt = nC[j + 1];
			DB _lose = dis(_lft, _cur) + dis(_cur, _nxt);
			vector<PT> _nC = getConvex(_lft, _cur, _nxt, cur);
			DB _get = 0;
			for (int k = 1; k < _nC.size(); k++) _get += dis(_nC[k - 1], _nC[k]);
			ans = max(ans, lose - get + _lose - _get);
		}
	}
	for (int i = 0; i < profit.size(); i++) {
		DB l = profit[(i - 1 + sz) % sz], c = profit[i], r = profit[(i + 1) % sz];
		PM.erase(PM.find(l)), PM.erase(PM.find(r)), PM.erase(PM.find(c));
		if (PM.size()) ans = max(ans, c + *(PM.rbegin()));
		else ans = max(ans, c);
		PM.insert(l), PM.insert(r), PM.insert(c);
	}
}

int main() {
	int n; scanf("%d", &n);
	PT p;
	while (n--) p.in(), pts.push_back(p);
	convex();
	process();
	printf("%.10lf\n", ans);
}
accuracy: 1
time_ms: 50
memory_kb: 4460
final_rank: 3

code_snippet: #include <bits/stdc++.h>
#include <sys/timeb.h>
using namespace std;
typedef long long LL;
struct P { int x, y; };
double len(P a) { return sqrt(1.0 * a.x * a.x + 1.0 * a.y * a.y); }
P operator + (P a, P b) { return {a.x + b.x, a.y + b.y}; }
P operator - (P a, P b) { return {a.x - b.x, a.y - b.y}; }
bool operator < (P a, P b) { return a.x == b.x ? a.y < b.y : a.x < b.x; }
bool operator == (P a, P b) { return a.x == b.x & a.y == b.y; }
double len(const vector<P> &a) {
	double res = 0;
	for (int i = 0; i < (int) a.size() - 1; ++ i) {
		res += len(a[i + 1] - a[i]);
	}
	return res;
}
LL det(P a, P b) { return 1LL * a.x * b.y - 1LL * a.y * b.x; }
int sign(LL x) { return (x > 0) - (x < 0); }
typedef vector<vector<P>> Convex;
#define sz(x) ((int) x.size())
int lb(P x, const vector<P> & v, int le, int ri, int sg) {
	if (le > ri) le = ri;
	int s(le), t(ri);
	while (le != ri) {
		int mid((le + ri) / 2);
		if (sign(det(v[mid] - x, v[mid + 1] - v[mid])) == sg)
			le = mid + 1; else ri = mid;
	}
	return le; // le 即为下标, 按需返回
}
// v[0] 为顺时针上凸壳, v[1] 为顺时针下凸壳, 均允许起始两个点横坐标相同
// 返回值为真代表严格在凸包外, 顺时针旋转在 d1 方向先碰到凸包
bool getTan(P x, const Convex & v, int & d1, int & d2) {
	if (x.x < v[0][0].x) {
		d1 = lb(x, v[0], 0, sz(v[0]) - 1, 1);
		d2 = lb(x, v[1], 0, sz(v[1]) - 1, -1) + (int) v[0].size() - 1;
		return true;
	} else if(x.x > v[0].back().x) {
		d1 = lb(x, v[1], 0, sz(v[1]) - 1, 1) + (int) v[0].size() - 1;
		d2 = lb(x, v[0], 0, sz(v[0]) - 1, -1);
		return true;
	} else {
		for(int d(0); d < 2; d++) {
			int id(lower_bound(v[d].begin(), v[d].end(), x,
						[&](const P & a, const P & b) {
						return d == 0 ? a < b : b < a;
						}) - v[d].begin());
			if (id && (id == sz(v[d]) || det(v[d][id - 1] - x, v[d][id] - x) > 0)) {
				d1 = lb(x, v[d], id, sz(v[d]) - 1, 1);
				d2 = lb(x, v[d], 0, id, -1);
				if (d) {
					d1 += (int) v[0].size() - 1;
					d2 += (int) v[0].size() - 1;
				}
				return true;
			}
		}
	}
	return false;
}
const int N = 1e5 + 5;
Convex build(vector<P> &a) {
	Convex res (2);
	sort(a.begin(), a.end());
	static int pop_count[N];
	int n = (int) a.size();
	vector<P> b;
	for (int i = 0; i < n; ++ i) {
		while (res[0].size() > 1 && det(a[i] - res[0][(int) res[0].size() - 2], res[0].back() - res[0][(int) res[0].size() - 2]) <= 0) {
			b.push_back(res[0].back());
			res[0].pop_back();
		}
		res[0].push_back(a[i]);
	}
	for (int i = n - 1; i >= 0; -- i) {
		while (res[1].size() > 1 && det(a[i] - res[1][(int) res[1].size() - 2], res[1].back() - res[1][(int) res[1].size() - 2]) <= 0) {
			b.push_back(res[1].back());
			res[1].pop_back();
		}
		res[1].push_back(a[i]);
	}
	sort(b.begin(), b.end());
	//printf("b = "); for (P x : b) printf("%d %d & ", x.x, x.y); puts("");
	vector<P> c;
	for (int i = 0; i + 1 < (int) b.size(); ++ i) {
		if (b[i] == b[i + 1]) c.push_back(b[i]);
	}
	swap(a, c);
	return res;
}
int size_Convex(const Convex &a) {
	if (a[0].empty()) return 0;
	int n = (int) a[0].size() + (int) a[1].size() - 2;
	return max(n, 1);
}
P get_Convex(const Convex &a, int i) {
	if (i < a[0].size()) return a[0][i];
	return a[1][i - (int) a[0].size() + 1];
}
int touch1, touch2;
vector<P> insert(P L, P R, const Convex & a) {
	touch1 = touch2 = -1;
	if (L == R || a[0].empty()) return {L, R};
	int d1, d2;
	//printf("L : %d %d  R : %d %d   cut : ", L.x, L.y, R.x, R.y);
	//for (int i = 0; i < size_Convex(a); ++ i) printf("%d %d & ", get_Convex(a, i).x, get_Convex(a, i).y); puts("");
	assert(getTan(L, a, d1, d2));
	if (det(get_Convex(a, d1) - L, R - L) >= 0) return {L, R};
	touch2 = touch1 = d1;
	int n = size_Convex(a);
	vector<P> res = {L, get_Convex(a, d1)};
	P las = res.back();
	for (int i = (d1 + 1) % n; det(get_Convex(a, i) - las, R - las) < 0; i = (i + 1) % n) {
		res.push_back(get_Convex(a, i));
		touch2 = i;
		las = res.back();
	}
	res.push_back(R);
	return res;
}
int main() {
	int n;
	scanf("%d", &n);
	//n = 10;
	// 10 4148335439 5001
	// 8 4148842258 501
	//struct timeb timeSeed;
	//ftime(&timeSeed);
	//unsigned int seed = timeSeed.time * 1000 + timeSeed.millitm;
	//int seed = time(0);
	//cout << seed << endl;
	//srand(seed);
	vector<P> all;
	for (int i = 0; i < n; ++ i) {
		int x, y;
		scanf("%d%d", &x, &y);
		//x = rand() % 50001;
		//y = rand() % 50001;
		//printf("%d %d\n", x, y);
		all.push_back({x, y});
	}
	sort(all.begin(), all.end()); all.erase(unique(all.begin(), all.end()), all.end());
	auto all2 = all;
	Convex A = build(all); int nA = size_Convex(A);
	//for (int i = 0; i < nA; ++ i) printf("%d %d & ", get_Convex(A, i).x, get_Convex(A, i).y); puts("");
	Convex B = build(all); int nB = size_Convex(B);
	//for (int i = 0; i < nB; ++ i) printf("%d %d & ", get_Convex(B, i).x, get_Convex(B, i).y); puts("");
	Convex C = build(all); int nC = size_Convex(C);
	//for (int i = 0; i < nC; ++ i) printf("%d %d & ", get_Convex(C, i).x, get_Convex(C, i).y); puts("");
	//printf("%d %d %d =======\n", nA, nB, nC);
	/*double DP = 0;
	for (int i = 0; i < all2.size(); ++ i) {
		for (int k = i; k < all2.size(); ++ k) {
		vector<P> bll;
		for (int j = 0; j < all2.size(); ++ j) if (i != j && k != j) bll.push_back(all2[j]);
		Convex con = build(bll);
		double tmp = len(A[0]) + len(A[1]) - len(con[0]) - len(con[1]);
		//if (tmp > DP) printf("DP %d %d  %d %d  %.10f\n", all2[i].x, all2[i].y, all2[k].x, all2[k].y, tmp);
		DP = max(DP, tmp);
		}
	}*/
	vector<pair<double, int>> val;
	double ans = 0;
	for (int i = 0; i < nA; ++ i) {
		P a = get_Convex(A, i), b = get_Convex(A, (i + 1) % nA), c = get_Convex(A, (i + 2) % nA);
		double res = len(a - b) + len(b - c);
		auto half = insert(a, c, B);
		res -= len(half);
		ans = max(ans, res);
		val.push_back({res, i});
		//printf("b = %d %d  res = %.10f\n", b.x, b.y, res);
		//for (P x : half) printf("%d %d & ", x.x, x.y); puts("");
	}
	sort(val.rbegin(), val.rend());
	for (int i = 0; i < nA; ++ i) {
		double res = val[i].first;
		set<int> adj = {val[i].second, (val[i].second + 1) % nA, (val[i].second - 1 + nA) % nA};
		for (int j = 0; j < nA; ++ j) {
			if (adj.count(val[j].second)) continue;
			ans = max(ans, res + val[j].first);
			break;
		}
	}
	if (nA >= 4) {
		for (int i = 0; i < nA; ++ i) {
			P a = get_Convex(A, i), b = get_Convex(A, (i + 1) % nA), c = get_Convex(A, (i + 2) % nA), d = get_Convex(A, (i + 3) % nA);
			double res = len(a - b) + len(b - c) + len(c - d);
			auto half = insert(a, d, B);
			//for (P x : half) printf("%d %d & ", x.x, x.y); puts("");
			res -= len(half);
			ans = max(ans, res);
			//printf("%d %d %d %d : %.10f\n", b.x, b.y, c.x, c.y, res);
		}
	}
	else {
		assert(nA == 3);
		for (int i = 0; i < nA; ++ i) {
			vector<P> tmp = B[0];
			for (P x : B[1]) tmp.push_back(x);
			tmp.push_back(get_Convex(A, i));
			Convex res = build(tmp);
			ans = max(ans, len(A[0]) + len(A[1]) - len(res[0]) - len(res[1]));
		}
	}
	for (int i = 0; i < nA; ++ i) {
		P a = get_Convex(A, i), b = get_Convex(A, (i + 1) % nA), c = get_Convex(A, (i + 2) % nA);
		double res = len(a - b) + len(b - c);
		auto half = insert(a, c, B);
		int touch1 = ::touch1;
		int touch2 = ::touch2;
		P X = b;
		//printf("touch %d %d\n", touch1, touch2);
		//printf("half : "); for (P x : half) printf("%d %d & ", x.x, x.y); puts("");
		res -= len(half);
		for (int j = 1; j < (int) half.size() - 1; ++ j) {
			P a = half[j - 1], b = half[j], c = half[j + 1];
			assert(touch1 != -1 && touch2 != -1);
			if (nB > 1) {
				if (j == 1) a = get_Convex(B, (touch1 + nB - 1) % nB);
				if (j == (int) half.size() - 2) c = get_Convex(B, (touch2 + 1) % nB);
			}
			double res2 = len(half[j - 1] - b) + len(b - half[j + 1]);
			auto half2 = insert(a, c, C);
			//printf("half2 : "); for (P x : half2) printf("%d %d & ", x.x, x.y); puts("");
			int l = 0, r = (int) half2.size() - 1;
			a = half[j - 1], c = half[j + 1];
			while (l < r && det(half2[l] - a, half2[l + 1] - a) >= 0) l ++;
			while (r > l && det(half2[r] - c, half2[r - 1] - c) <= 0) r --;
			//printf("%d %d\n", l, r);
			vector<P> half3 = {a};
			for (int k = l; k <= r; ++ k) half3.push_back(half2[k]);
			if (l == r && det(c - a, half3.back() - a) <= 0) half3.pop_back();
			half3.push_back(c);
			//printf("half3 : "); for (P x : half3) printf("%d %d & ", x.x, x.y); puts("");
			res2 -= len(half3);
			//printf("%d %d   %d %d  %.5f\n", X.x, X.y, b.x, b.y, res + res2);
			ans = max(ans, res + res2);
		}
	}
	printf("%.10f\n", ans);
	//printf("%.10f\n", DP);
	//if (fabs(ans - DP) > 1e-4) while (1);
}
accuracy: 1
time_ms: 80
memory_kb: 8840
final_rank: 6