#include using namespace std; #define REP(i,a,b) for(int i=a;i<(int)b;i++) #define rep(i,n) REP(i,0,n) typedef double Real; // typedef long double Real; typedef complex P; namespace std { bool operator < (P const& a, P const& b) { return real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b); } } Real dot(P const& a, P const& b) { return real(conj(a)*b); } Real cross(P const& a, P const& b) { return imag(conj(a)*b); } enum { counter_clockwise=+1, clockwise=-1, cab_online=+2, abc_online=-2, on_segment = 0 }; int ccw(P a, P b, P c) { b -= a; c -= a; if(cross(b, c) > 0) return counter_clockwise; if(cross(b, c) < 0) return clockwise; if(dot(b, c) < 0) return cab_online; if(norm(b) < norm(c)) return abc_online; return on_segment; } typedef vector

Polygon; #define prev(G, i) ( G[(i-1 + G.size()) % G.size()] ) #define curr(G, i) ( G[i % G.size()] ) #define next(G, i) ( G[(i+1) % G.size()] ) Polygon convex_hull(vector

ps) { int n = ps.size(), k = 0; sort(ps.begin(), ps.end()); vector

ch(2*n); for (int i = 0; i < n; ch[k++] = ps[i++]) // lower-hull while (k >= 2 && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k; for (int i = n-2, t = k+1; i >= 0; ch[k++] = ps[i--]) // upper-hull while (k >= t && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k; ch.resize(k-1); return ch; } Real convex_naive_diameter(Polygon const& g) { int N = g.size(); Real ret = 0.; rep(i, N) REP(j, i, N) { ret = max(ret, abs(g[i]-g[j])); } return ret; } #define diff(G, i) (next(G, i) - curr(G, i)) // 対蹠点対のみを走査して O(n) キャリバー法 Real convex_diameter(Polygon const& g) { int const N = g.size(); int is = 0, js = 0; REP(i, 1, N) { if(imag(g[i]) > imag(g[is])) { is = i; } if(imag(g[i]) < imag(g[js])) { js = i; } } Real mxd = norm(g[is]-g[js]); int i, mxi, j, mxj; i = mxi = is; j = mxj = js; do { if(cross(diff(g, i), diff(g, j)) >= 0) { j = (j+1) % N; } else { i = (i+1) % N; } if(norm(g[i]-g[j]) > mxd) { mxd = norm(g[i]-g[j]); mxi = i, mxj = j; } } while(i != is || mxj != js); return mxd; } inline bool inrange(int x, int y, int W, int H) { return 0<=x&&x rank; vector par; UnionFind(int n) { rank.resize(n), par.resize(n); rep(i, n) { par[i] = i; rank[i] = 0; } } int find(int x) { if(par[x] == x) { return x; } else { 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]) { par[x] = y; } else { par[y] = x; if(rank[x] == rank[y]) { rank[x] ++; } } } bool same(int x, int y) { return find(x) == find(y); } }; vector

ps; int constexpr Offset = 10000; int constexpr MaxSize = 21000; int constexpr BSize = 100; vector bucket[MaxSize/BSize][MaxSize/BSize]; int main() { int N; cin >> N; if(N == 0) { cout << 1 << endl; return 0; } //if(N == 1) { cout << 10 << endl; return 0; } rep(i, N) { double x, y; cin >> x >> y; //cerr << x << ", " << y << " " << Offset << endl; ps.push_back(P(x+Offset, y+Offset)); } /* if(N == 2) { if(abs(ps[0]-ps[1]) <= 10.) { cerr << ps[0] << " " << ps[1] << endl; cerr << abs(ps[0]-ps[1]) << endl; printf("%.10f\n", abs(ps[0]-ps[1])+10); return 0; } } */ rep(i, N) { int bx = ps[i].real()/BSize; int by = ps[i].imag()/BSize; bucket[by][bx].push_back(i); } UnionFind uf(2*N); rep(i, N) { int bx = ps[i].real()/BSize; int by = ps[i].imag()/BSize; REP(outy, -1, 2) REP(outx, -1, 2) { int x = bx+outx, y = by+outy; if(inrange(x, y, MaxSize/BSize, MaxSize/BSize)) { for(auto j: bucket[y][x]) { //cerr << "bucket: " << x << ", "<< y << endl; //cerr << "ps: " << i << ", " << j << endl; if(abs(ps[i]-ps[j]) <= 10.) { uf.unite(i, j); } } } } } vector > clusters(N); rep(i, N) { clusters[uf.find(i)].push_back(ps[i]); } Real ans = 0.; for(auto& g: clusters) { if(g.empty()) { continue; } //Real dia = convex_diameter(g); g = convex_hull(g); Real dia = convex_naive_diameter(g); ans = max(ans, dia+2); } printf("%.10f\n", ans); return 0; }