Codeforces Round #194 Div1E Summer Earnings
問題
N個の点の座標(整数)が与えられます。
この中から3点を選んで、その3点を中心として半径rの円を描きます。
このとき円は接してもいいけど交差しちゃダメです。
とりうるrの最大値を求めなさい。
制約
(3 ≤ n ≤ 3000)
( - 10^4 ≤ xi, yi ≤ 10^4)
解法
3点を選んだ時のrの最大値は、(3点がつくる三角形の一番短い辺の長さ)/2 です。
というわけで、最短辺が最長になる三角形をこれらN個の点から求める問題になります。
editorialを見ると、△ABCの∠Bが60度以上(最小角ではない)とき、BCが最短辺とならないことを利用し、
1.Bを固定。
2.Bに対する角度で残りの点をソートし、60度以上の角度を持つ2点A,Cを選ぶ。選び方はセグメント木を使うのでO(nlogn)に抑えられる。
というようなことが書いてありました(若干怪しいというか、正直あんまりしっかり理解できてないですが…)。
しかし、もう少し愚直な方法を頑張っても通ってしまいました。というのも、
「全点間距離を計算し、距離が長い順に線を描いていき、三角形ができたら終了」
というものです。
最悪ケースは1500個ずつの2つのクラスタが出来ている場合で、1500*1500の辺を描いてもまだ三角形ができない場合があります。
「もう三角形ができているか」を判定するのにO(n)かかるとすると、この解法はO(n^3)なのですが、
この判定をbitsetを使って定数倍高速化するとなんと間に合ってしまいます。
(この問題のTime limitは9秒あり、最大ケースで3秒強かかっていました)
プログラム
いつものクセでx,yとかに無駄にlong longを使ったらMLEになる程度には制約厳しかったです。
#include<iostream> #include <fstream> #include <stdio.h> #include <stdlib.h> #define _USE_MATH_DEFINES #include <math.h> #include<string> #include<vector> #include<cmath> #include<stack> #include<queue> #include<sstream> #include<algorithm> #include<map> #include<complex> #include<bitset> using namespace std; //#define int long long int #define rep(i,to) for(int i=0;i<((int)(to));i++) #define repp(i,start,to) for(int i=(int)(start);i<((int)(to));i++) #define pb push_back #define sz(v) ((int)(v).size()) #define bgn(v) ((v).begin()) #define eend(v) ((v).end()) #define allof(v) (v).begin(), (v).end() #define dodp(v,n) memset(v,(int)n,sizeof(v)) #define bit(n) (1ll<<(int)(n)) #define mp(a,b) make_pair(a,b) #define rin rep(i,n) #define EPS 1e-10 #define ETOL 1e-8 #define MOD 1000000007 #define F first #define S second #define p2(a,b) cout<<a<<"\t"<<b<<endl #define p3(a,b,c) cout<<a<<"\t"<<b<<"\t"<<c<<endl int x[3010], y[3010]; bitset<3030> ks[3030]; vector<pair<int, pair<int,int> > > v; int len(int i, int j){ return (x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]); } int main(){ int n; cin>>n; rin{ cin>>x[i]>>y[i]; } rep(i,n)rep(j,n)ks[i]=0; rep(i,n-1)repp(j,i+1,n)v.pb(mp(len(i,j), mp(i,j))); sort(allof(v)); for(int i=sz(v)-1; i>=0; i--){ int v1=v[i].S.F, v2=v[i].S.S; if((ks[v1]&ks[v2]).any()){ printf("%.8f\n",sqrt((double)v[i].F/4.0)); return 0; } ks[v2][v1]=ks[v1][v2]=true; } return 0; }