結果
問題 | No.96 圏外です。 |
ユーザー |
![]() |
提出日時 | 2015-08-04 23:13:44 |
言語 | C++11 (gcc 13.3.0) |
結果 |
AC
|
実行時間 | 549 ms / 5,000 ms |
コード長 | 8,078 bytes |
コンパイル時間 | 2,071 ms |
コンパイル使用メモリ | 183,184 KB |
実行使用メモリ | 108,988 KB |
最終ジャッジ日時 | 2024-12-24 08:16:02 |
合計ジャッジ時間 | 8,430 ms |
ジャッジサーバーID (参考情報) |
judge2 / judge5 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
other | AC * 28 |
ソースコード
#include <bits/stdc++.h>typedef long long ll;typedef unsigned long long ull;#define FOR(i,a,b) for(int (i)=(a);i<(b);i++)#define REP(i,n) FOR(i,0,n)#define RANGE(vec) (vec).begin(),(vec).end()using namespace std;template<typename T>struct Pt {T x, y;Pt(T x0, T y0) : x(x0), y(y0) {}Pt() :x(0),y(0) {}Pt(const Pt &other) :x(other.x),y(other.y) {}Pt &operator=(const Pt& other) { x = other.x; y = other.y; return *this; }const Pt operator+(const Pt &other) const { return Pt(other.x+x, other.y+y); }const Pt operator-(const Pt &other) const { return Pt(other.x-x, other.y-y); }Pt &operator+=(const Pt &other) { x += other.x; y += other.y; return *this; }Pt &operator-=(const Pt &other) { x -= other.x; y -= other.y; return *this; }Pt operator*(double r) const { return Pt(x*r, y*r); }bool operator==(const Pt &other) const { return (x==other.x && y==other.y); }bool operator!=(const Pt &other) const { return !(operator==(other)); }bool operator<(const Pt &other) const { return (x < other.x)? true : ((x==other.x)? (y < other.y) : false); }bool operator<=(const Pt &other) const { return (*this == other)? true : (*this < other); }bool operator>(const Pt &other) const { return (other < *this); }bool operator>=(const Pt &other) const { return (other <= *this); }double norm(void) const { return hypot(x, y); }// class methodstatic double cross(const Pt &a, const Pt &b) { return ((double)a.y*b.x - (double)a.x*b.y); }static double dot(const Pt &a, const Pt &b) { return (double)a.x*b.x+(double)a.y*b.y; }// O(n*log(n))static std::vector<Pt> convex_hull(std::vector<Pt> &ps) {std::sort(ps.begin(), ps.end());int k = 0;int n = ps.size();vector<Pt> qs(2*n);for (int i = 0; i < n; ++i){while (k > 1 && cross(qs[k-1]-qs[k-2], ps[i]-qs[k-1]) <= 0)--k;qs[k++] = ps[i];}for (int i = n-2, t = k; i >= 0; --i){while (k > t && cross(qs[k-1]-qs[k-2], ps[i]-qs[k-1]) <= 0)--k;qs[k++] = ps[i];}qs.resize(k-1);qs.shrink_to_fit();return std::move(qs);}// convex_diameter// O(n*log(n))static double convex_diameter(std::vector<Pt> &ps) {auto qs = convex_hull(ps);int n = qs.size();auto dist = [](const Pt &a, const Pt &b) { return sqrt(dot(a-b, a-b)); };if (n == 2)return dist(qs[0], qs[1]);int i,j;i = j = 0;for (int k = 0; k < n; ++k){if ( !(qs[i] < qs[k]) )i = k;if (qs[j] < qs[k])j = k;}double res = 0;int si = i, sj = j;while ( !(i == sj && j == si) ){res = max(res, dist(qs[i], qs[j]));int di = (i+1)%n;int dj = (j+1)%n;if (cross(qs[di]-qs[i], qs[dj]-qs[j]) < 0)i = di;elsej = dj;}return res;}friend std::ostream &operator<<(std::ostream &os, const Pt &p) {os <<"("<<p.x<<","<<p.y<<")";return os;}};template<typename T>Pt<T> operator*(double r, const Pt<T> &p) { return p*r; }class UFTree {std::vector<int> par_;std::vector<int> rank_;int size_;public:UFTree(int N) { init(N); }UFTree() {}~UFTree() {}void init(int N) {par_.resize(N);rank_.resize(N, 0);for (int i = 0; i < N; ++i)par_[i] = i;size_ = N;}int find(int x) {assert(0 <= x && x < par_.size());if ( par_[x] == x )return x;elsereturn ( par_[x] = find(par_[x]));}void unite(int x, int y) {assert(0 <= x && x < par_.size());assert(0 <= y && y < par_.size());x = find(x);y = find(y);if ( x == y )return;--size_;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));}size_t size() { return size_; }// find 経由で par を更新してからでないと par へのアクセスは危険int operator[](size_t i) { return find(i); }};class NoService {public:void solve(void) {int N;cin>>N;// 中継局がないときは 1kmif (N==0){cout<<1<<endl;return;}//// Easy と違って N <= 120000 なので O(N^2) では間に合わない// 距離が 10 km 以内の中継局を同じグループとして、中継局をグループ化してやることが// できれば、凸包を作って(O(n*log(n)) からキャリパー法 (O(n)) が使える。//// union find 自体は高速なので、問題は (x,y) の周り 10km の (x',y') をどう求めるか// が問題となる。10km 以内の中継局を同じグループとみなすので、座標 x,y を// 10km x 10km のバケットで管理するバケット法を使う。//// 座標は整数なので 1 バケットに最大 100 個の点が入る。// (x,y) の周囲 10km 以内の点を見つけるのには (x,y) の所属するバケット周りの 9 バケット// (最大 900 点)をチェックすればよい。//// O(N*900) <= 108000000//// 制限時間 5 秒なのでなんとかまにあいそう?//// x,y の制限が小さいので配列でバケットが作れる (access O(1))UFTree uft(N);const int offset = 10000;const int B = 10;const int dx[] = {-1,0,1};const int dy[] = {-1,0,1};const int M = 2*offset/B; // 2000vector<vector<vector<int>>> bucket(M+1,vector<vector<int>>(M+1, vector<int>()));vector<Pt<int>> ps;REP(i,N){int x,y;cin>>x>>y;ps.emplace_back(x,y);bucket[(x+offset)/B][(y+offset)/B].push_back(i);}// bucket を使って UFTree を更新REP(i,N){int xi = (ps[i].x+offset)/B;int yi = (ps[i].y+offset)/B;// 隣接する 9 バケットを探索REP(ii,3)REP(jj,3){int xi2 = xi+dx[ii];int yi2 = yi+dy[jj];if (xi2 < 0 || xi2 > M || yi2 < 0 || yi2 > M)continue;for (auto k : bucket[xi2][yi2]){if (k == i)continue;if ( (ps[k]-ps[i]).norm() <= 10 )uft.unite(k,i);}}}vector<vector<Pt<int>>> group(N);REP(i,N)group[uft[i]].push_back(ps[i]);double ans = 0;REP(i,N){if (uft[i]==i)ans = max(ans, Pt<int>::convex_diameter(group[uft[i]]));}cout<<setprecision(20)<<ans+2<<endl;}};#if 1int main(int argc, char *argv[]){ios::sync_with_stdio(false);auto obj = new NoService();obj->solve();delete obj;return 0;}#endif