Problem:
Problem H:
Robot's Crash
Prof. Jenifer A. Gibson is carrying out experiments with many robots. Since those robots are
expensive, she wants to avoid their crashes during her experiments at her all effort. So she asked
you, her assistant, as follows.
“Suppose that we have
n
(2 ≤
n
≤ 100000) robots of the circular shape with the radius of
r
,
and that they are placed on the
xy
-plane without overlaps. Each robot starts to move straight
with a velocity of either
v
or -
v
simultaneously, say, at the time of zero. The robots keep their
moving infinitely unless I stop them. I’d like to know in advance if some robots will crash each
other. The robots crash when their centers get closer than the distance of 2
r
. I’d also like to
know the time of the first crash, if any, so I can stop the robots before the crash. Well, could
you please write a program for this purpose?”
Input
The input consists of multiple datasets. Each dataset has the following format:
n
vx vy
r
rx
1
ry
1
u
1
rx
2
ry
2
u
2
...
rx
n
ry
n
u
n
n
is the number of robots. (
vx
,
vy
) denotes the vector of the velocity
v
.
r
is the radius of the
robots. (
rx
i
,
ry
i
) denotes the coordinates of the center of the
i
-th robot.
u
i
denotes the moving
direction of the
i
-th robot, where 1 indicates the
i
-th robot moves with the velocity (
vx
,
vy
),
and -1 indicates (-
vx
, -
vy
).
All the coordinates range from -1200 to 1200. Both
vx
and
vy
range from -1 to 1.
You may assume all the following hold for any pair of robots:
they must not be placed closer than the distance of (2
r
+ 10
-8
) at the initial state;
they must get closer than the distance of (2
r
- 10
-8
) if they crash each other at some
  point of time; and
they must not get closer than the distance of (2
r
+ 10
-8
) if they don’t crash.
The input is terminated by a line that contains a zero. This should not be processed.
Output
For each dataset, print the first crash time in a line, or “SAFE” if no pair of robots crashes each
other. Each crash time should be given as a decimal with an arbitrary number of fractional
digits, and with an absolute error of at most 10
-4
.
Sample Input
2
1.0 0.0
0.5
0.0 0.0 1
2.0 0.0 -1
2
1.0 0.0
0.5
0.0 0.0 -1
2.0 0.0 1
0
Output for the Sample Input
0.500000
SAFE

Submitted solutions:

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define chmin(x,y) x = min(x,y)
#define chmax(x,y) x = max(x,y)
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 998244353
//#define MOD 1000000007
#define EPS 1e-12




#define SIZE 100005

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;



int N,DIR[SIZE];
double baseR;
Point point[SIZE];

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

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

//点のradを求める関数
double calc_rad(Point p){

	if(p.y == 0){

		if(p.x > 0){

			return 0;

		}else{ //p.x < 0

			return M_PI;
		}

	}else if(p.x == 0){

		if(p.y > 0){

			return M_PI/2.0;
		}else{


			return 3*M_PI/2.0;
		}

	}else{

		double base = atan(fabs(p.y)/fabs(p.x));

		if(p.x > 0){

			if(p.y > 0){

				return base;

			}else{ //p.y < 0

				return 2*M_PI-base;
			}

		}else{ //p.x < 0

			if(p.y > 0){

				return M_PI-base;

			}else{

				return base+M_PI;
			}
		}
	}
}

/*点moveをbaseを中心にradラジアン回転させる
rad > 0なら反時計回り、rad < 0なら時計周り
*/
Point rotate(Point base,Point move,double rad){

	Point ret;
	move = move-base;

	ret.x = move.x*cos(rad)-move.y*sin(rad);
	ret.y = move.x*sin(rad)+move.y*cos(rad);

	ret = ret+base;

	return ret;
}

void func(){

	double vx,vy;
	scanf("%lf %lf %lf",&vx,&vy,&baseR);

	double rad = calc_rad(Point(vx,vy));

	double V = sqrt(vx*vx + vy*vy); //■正が右向き

	double rx,ry;
	Point center = Point(0,0);

	vector<pair<double,int>> vec;

	for(int i = 0; i < N; i++){

		scanf("%lf %lf %d",&rx,&ry,&DIR[i]);
		point[i] = rotate(center,Point(rx,ry),-rad);

		vec.push_back(make_pair(point[i].y,i));
	}

	sort(vec.begin(),vec.end()); //y座標の昇順にする

	set<pair<double,int>> L,R;

	double ans = HUGE_NUM;

	int IND = 0;

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

		int ind = vec[i].second;
		double y = point[ind].y;

		//衝突の可能性がなくなったロボットを削除
		while(IND < i && (y-vec[IND].first) > 2*baseR+EPS){

			int tmp_ind = vec[IND].second;
			if(DIR[tmp_ind] == -1){ //左向き

				auto at = L.lower_bound(make_pair(point[tmp_ind].x-EPS,-1));

				while(at != L.end()){

					if(at->second == tmp_ind){
						L.erase(at);
						break;
					}
					at++;
				}

			}else{ //右向き

				auto at = R.lower_bound(make_pair(point[tmp_ind].x-EPS,-1));

				while(at != R.end()){

					if(at->second == tmp_ind){
						R.erase(at);
						break;
					}
					at++;
				}
			}
			IND++;
		}

		//■先に登録
		if(DIR[ind] == -1){

			L.insert(make_pair(point[ind].x,ind));

		}else{

			R.insert(make_pair(point[ind].x,ind));
		}

		Point a,b;
		if(DIR[ind] == 1){

			auto at = L.lower_bound(make_pair(point[ind].x-EPS,-1));
			if(at == L.end()){

				continue;
			}

			a = point[ind];
			b = point[at->second];

		}else{

			auto at = R.lower_bound(make_pair(point[ind].x-EPS,-1));
			if(at == R.begin()){

				continue;
			}

			at--;

			a = point[at->second];
			b = point[ind];
		}

		double A = 4*V*V;
		double B = 4*(a.x-b.x)*V;
		double C = a.x*a.x - 2*a.x*b.x + b.x*b.x - (4*baseR*baseR-(a.y-b.y)*(a.y-b.y));

		double D = B*B-4*A*C;
		if(D+EPS < 0){

			continue; //解なし
		}

		double t1 = (-B+sqrt(D))/(2*A);
		if(t1 > EPS){

			chmin(ans,t1);
		}

		double t2 = (-B-sqrt(D))/(2*A);
		if(t2 > EPS){

			chmin(ans,t2);
		}
	}

	if(fabs(ans-HUGE_NUM) < EPS){

		printf("SAFE\n");

	}else{

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


int main(){

	while(true){

		scanf("%d",&N);
		if(N == 0)break;

		func();
	}

	return 0;
}
accuracy: 1
time_ms: 200
memory_kb: 13616
final_rank: 3

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define chmin(x,y) x = min(x,y)
#define chmax(x,y) x = max(x,y)
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 998244353
//#define MOD 1000000007
#define EPS 1e-12




#define SIZE 100005

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;



int N,DIR[SIZE];
double baseR;
Point point[SIZE];

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

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

//点のradを求める関数
double calc_rad(Point p){

	if(p.y == 0){

		if(p.x > 0){

			return 0;

		}else{ //p.x < 0

			return M_PI;
		}

	}else if(p.x == 0){

		if(p.y > 0){

			return M_PI/2.0;
		}else{


			return 3*M_PI/2.0;
		}

	}else{

		double base = atan(fabs(p.y)/fabs(p.x));

		if(p.x > 0){

			if(p.y > 0){

				return base;

			}else{ //p.y < 0

				return 2*M_PI-base;
			}

		}else{ //p.x < 0

			if(p.y > 0){

				return M_PI-base;

			}else{

				return base+M_PI;
			}
		}
	}
}

/*点moveをbaseを中心にradラジアン回転させる
rad > 0なら反時計回り、rad < 0なら時計周り
*/
Point rotate(Point base,Point move,double rad){

	Point ret;
	move = move-base;

	ret.x = move.x*cos(rad)-move.y*sin(rad);
	ret.y = move.x*sin(rad)+move.y*cos(rad);

	ret = ret+base;

	return ret;
}


bool DEBUG = false;

void func(){

	double vx,vy;
	scanf("%lf %lf %lf",&vx,&vy,&baseR);

	double rad = calc_rad(Point(vx,vy));

	if(DEBUG){

		printf("rad:%.3lf",rad);
	}

	double V = sqrt(vx*vx + vy*vy); //■正が右向き


	double rx,ry;
	Point center = Point(0,0);

	vector<pair<double,int>> vec;

	for(int i = 0; i < N; i++){

		scanf("%lf %lf %d",&rx,&ry,&DIR[i]);
		point[i] = rotate(center,Point(rx,ry),-rad);

		if(DEBUG){

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

		vec.push_back(make_pair(point[i].y,i));
	}

	sort(vec.begin(),vec.end()); //y座標の昇順にする

	set<pair<double,int>> L,R;

	double ans = HUGE_NUM;

	int IND = 0;

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

		int ind = vec[i].second;
		double y = point[ind].y;

		//衝突の可能性がなくなったロボットを削除
		while(IND < i && (y-vec[IND].first) > 2*baseR+EPS){

			int tmp_ind = vec[IND].second;
			if(DIR[tmp_ind] == -1){ //左向き

				auto at = L.lower_bound(make_pair(point[tmp_ind].x-EPS,-1));

				while(at != L.end()){

					if(at->second == tmp_ind){
						L.erase(at);
						break;
					}
					at++;
				}

			}else{ //右向き

				auto at = R.lower_bound(make_pair(point[tmp_ind].x-EPS,-1));

				while(at != R.end()){

					if(at->second == tmp_ind){
						R.erase(at);
						break;
					}
					at++;
				}
			}
			IND++;
		}

		//■先に登録
		if(DIR[ind] == -1){

			L.insert(make_pair(point[ind].x,ind));

		}else{

			R.insert(make_pair(point[ind].x,ind));
		}

		Point a,b;
		if(DIR[ind] == 1){

			auto at = L.lower_bound(make_pair(point[ind].x-EPS,-1));
			if(at == L.end()){
				if(DEBUG){

					printf("左向きなし\n");
				}
				continue;
			}

			a = point[ind];
			b = point[at->second];

		}else{

			auto at = R.lower_bound(make_pair(point[ind].x-EPS,-1));
			if(at == R.begin()){
				if(DEBUG){

					printf("右向きなし\n");
				}
				continue;
			}

			at--;

			a = point[at->second];
			b = point[ind];
		}

		double A = 4*V*V;
		double B = 4*(a.x-b.x)*V;
		double C = a.x*a.x - 2*a.x*b.x + b.x*b.x - (4*baseR*baseR-(a.y-b.y)*(a.y-b.y));

		double D = B*B-4*A*C;
		if(D+EPS < 0){
			if(DEBUG){

				printf("解なし\n");
			}

			continue; //解なし
		}

		double t1 = (-B+sqrt(D))/(2*A);
		if(t1 > EPS){

			chmin(ans,t1);
		}

		double t2 = (-B-sqrt(D))/(2*A);
		if(t2 > EPS){

			chmin(ans,t2);
		}
	}

	if(fabs(ans-HUGE_NUM) < EPS){

		printf("SAFE\n");

	}else{

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


int main(){

	while(true){

		scanf("%d",&N);
		if(N == 0)break;

		func();
	}

	return 0;
}
accuracy: 1
time_ms: 200
memory_kb: 13672
final_rank: 4

code_snippet: #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<list>
#include<queue>
#include<deque>
#include<algorithm>
#include<numeric>
#include<utility>
#include<complex>
#include<functional>
 
using namespace std;

/* constant */

const int MAX_N = 100000;

const double INF = 1e70;

/* typedef */

template <typename T>
struct Pt {
  T x, y;

  Pt() {}
  Pt(T _x, T _y) : x(_x), y(_y) {}
  Pt(const Pt& pt) : x(pt.x), y(pt.y) {}

  bool operator==(const Pt pt) const { return x == pt.x && y == pt.y; }
  Pt<T> operator+(const Pt pt) const { return Pt<T>(x + pt.x, y + pt.y); }
  Pt<T> operator-() const { return Pt<T>(-x, -y); }
  Pt<T> operator-(const Pt pt) const { return Pt<T>(x - pt.x, y - pt.y); }
  Pt<T> operator*(T t) const { return Pt<T>(x * t, y * t); }
  Pt<T> operator/(T t) const { return Pt<T>(x / t, y / t); }
  T dot(Pt v) const { return x * v.x + y * v.y; }
  T cross(Pt v) const { return x * v.y - y * v.x; }
  Pt<T> mid(const Pt pt) { return Pt<T>((x + pt.x) / 2, (y + pt.y) / 2); }
  T d2() { return x * x + y * y; }
  double d() { return sqrt(d2()); }

  Pt<T> rot(double th) {
    double c = cos(th), s = sin(th);
    return Pt<T>(c * x - s * y, s * x + c * y);
  }

  bool operator<(const Pt& pt) const {
    return x < pt.x || (x == pt.x && y < pt.y);
  }

  void print(string format) {
    printf(("(" + format + ", " + format + ")\n").c_str(), x, y);
  }
  void print() { print("%.6lf"); }
};

typedef Pt<double> pt;

struct Robot {
  pt p;
  int drc;
  Robot() {}
  Robot(pt _p, int _drc) : p(_p), drc(_drc) {}
  bool operator<(const Robot& r0) const { return p.x < r0.p.x; }
};

typedef set<Robot> srbt;

/* global variables */

int n;
pt v;
double r;
Robot rbts[MAX_N];

/* subroutines */

bool lessy(const Robot& r0, const Robot& r1) { return r0.p.y < r1.p.y; }

/* main */

int main() {
  for (;;) {
    cin >> n;
    if (n == 0) break;

    cin >> v.x >> v.y >> r;

    double th = -atan2(v.y, v.x);
    pt v0 = v.rot(th);
    //v0.print(); printf("th=%.6lf\n", th);

    double rr = r * 2;
    double dvx = v0.x * 2;
    
    for (int i = 0; i < n; i++) {
      pt p;
      cin >> p.x >> p.y >> rbts[i].drc;
      rbts[i].p = p.rot(th);
    }
    sort(rbts, rbts + n, lessy);
    
    double min_t = INF;
    srbt srl, srr;
    
    for (int i0 = 0, i1 = 0; i1 < n; i1++) {
      Robot& r1 = rbts[i1];

      while (i0 < i1 && r1.p.y - rbts[i0].p.y > rr) {
	if (rbts[i0].drc > 0) srl.erase(rbts[i0]);
	else srr.erase(rbts[i0]);
	i0++;
      }
      
      if (r1.drc < 0) {
	if (! srl.empty()) {
	  srbt::iterator rit = srl.lower_bound(r1);
	  if (rit != srl.begin()) {
	    rit--;
	    pt dp = r1.p - rit->p;
	    double t = (dp.x - sqrt(rr * rr - dp.y * dp.y)) / dvx;
	    if (min_t > t) min_t = t;
	  }
	}

	srr.insert(r1);
      }
      else {
	if (! srr.empty()) {
	  srbt::iterator rit = srr.lower_bound(r1);
	  if (rit != srr.end()) {
	    pt dp = rit->p - r1.p;
	    double t = (dp.x - sqrt(rr * rr - dp.y * dp.y)) / dvx;
	    if (min_t > t) min_t = t;
	  }
	}

	srl.insert(r1);
      }
    }

    if (min_t >= INF)
      cout << "SAFE" << endl;
    else 
      printf("%.6lf\n", min_t);
  }

  return 0;
}
accuracy: 1
time_ms: 650
memory_kb: 9948
final_rank: 2

code_snippet: #include <bits/stdc++.h>
#define REP(i,n) for(int i=0; i<(int)(n); ++i)

using namespace std;

typedef complex<double> Point;
namespace std{
    bool operator < (const Point& p1, const Point& p2){
        if(p1.real() != p2.real()) return p1.real() < p2.real();
        return p1.imag() < p2.imag();
    }
};
struct Event{
    double y;
    Point p;
    int p_type;
    int e_type;
    Event(double y, Point p, int p_type, int e_type) : 
        y(y), p(p), p_type(p_type), e_type(e_type) {}
    bool operator < (const Event& e) const{
        if(y != e.y) return y < e.y;
        if(e_type != e.e_type) return e_type > e.e_type; // delete is first ???
        return p < e.p;
    }
};


double get_time(Point p, Point q, double R, double v){
    double dx = abs(p.real() - q.real());
    double dy = abs(p.imag() - q.imag());
    if(!(2 * R >= dy)) return 1e16;
    assert(2 * R >= dy);
    double b = sqrt(4 * R * R - dy * dy);
    assert(dx - b >= 0);
    return (dx - b) / v;
}

void update(double& ans, vector<Point>& pset, Point p, double R, double v, int t){
    int k = lower_bound(pset.begin(), pset.end(), p) - pset.begin();
    for(int dk = -1; dk <= 1; dk++) if(k + dk >= 0 && k + dk < pset.size()){
        if(t == 0 && p.real() < pset[k + dk].real()){
            ans = min(ans, get_time(p, pset[k + dk], R, v));
        }else if(t == 1 && p.real() > pset[k + dk].real()){
            ans = min(ans, get_time(p, pset[k + dk], R, v));
        }
    }
}

int main(){
    int N;
    while(cin >> N && N){
        double vx, vy;
        cin >> vx >> vy;
        vx *= 2; vy *= 2;
        double th = arg(Point(vx, vy));
        double v = sqrt(vx * vx + vy * vy);
        double R;
        cin >> R;

        vector<Event> events;

        REP(i, N){
            double x, y;
            int u;
            cin >> x >> y >> u;
            Point p(x, y);
            p = p * polar(1.0, -th);
            if(u == 1){
                events.push_back(Event(p.imag() - R, p, 0, 0));
                events.push_back(Event(p.imag() + R, p, 0, 1));
            }else{
                events.push_back(Event(p.imag() - R, p, 1, 0));
                events.push_back(Event(p.imag() + R, p, 1, 1));
            }
        }

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

        vector<Point> point_set[2];
        double ans = 1e16;
        for(int i = 0; i < events.size(); i++){
            Event e = events[i];
            //cout << e.p << " " << e.e_type << " " << e.p_type << endl;
            vector<Point>& my_set = point_set[e.p_type];
            vector<Point>& you_set = point_set[e.p_type ^ 1];
            if(e.e_type == 0){
                my_set.insert(lower_bound(my_set.begin(), my_set.end(), e.p), e.p);
                update(ans, you_set, e.p, R, v, e.p_type);
            }else if(e.e_type == 1){
                my_set.erase(lower_bound(my_set.begin(), my_set.end(), e.p));
                int k = lower_bound(you_set.begin(), you_set.end(), e.p) - you_set.begin();
                for(int dk = -1; dk <= 1; dk ++) if(k + dk >= 0 && k + dk < you_set.size()){
                    update(ans, my_set, you_set[k], R, v, e.p_type ^ 1);
                }
            }
        }
        if(ans >= 1e16){
            puts("SAFE");
        }else{
            printf("%.6f\n", ans);
        }
    }
    return 0;
}
accuracy: 1
time_ms: 2760
memory_kb: 15680
final_rank: 5

code_snippet: #include<set>
#include<cmath>
#include<cstdio>
#include<algorithm>

#define rep(i,n) for(int i=0;i<(n);i++)

using namespace std;

const double INF=1e77;
const double PI=acos(-1);

template<class T>
struct point{ T x,y; };

template<class T>
double abs(const point<T> &a){ return sqrt(a.x*a.x+a.y*a.y); }

template<class T>
double arg(const point<T> &a,const point<T> &b){
	double ta=atan2(a.y,a.x);
	double tb=atan2(b.y,b.x);
	return tb-ta<0 ? tb-ta+2*PI : tb-ta;
}

point<double> rot(const point<double> &a,double theta){
	return (point<double>){a.x*cos(theta)-a.y*sin(theta),a.x*sin(theta)+a.y*cos(theta)};
}

struct robot{
	point<double> p; // 位置
	int dir; // 移動方向 (+1 or -1)
	bool operator<(const robot &Z)const{ return p.x<Z.p.x; }
};

// 速さ v で動くロボット A, B が衝突するときの時刻
double f(const robot &A,const robot &B,double r,double v){
	double w=abs(A.p.x-B.p.x);
	double h=sqrt(4*r*r-w*w);
	return (abs(A.p.y-B.p.y)-h)/(2*v);
}

int main(){
	for(int n;scanf("%d",&n),n;){
		point<double> v;
		double r; scanf("%lf%lf%lf",&v.x,&v.y,&r);
		double v_abs=abs(v);

		static robot Z[100000];
		rep(i,n) scanf("%lf%lf%d",&Z[i].p.x,&Z[i].p.y,&Z[i].dir);

		// y 軸の +/- 方向に移動するように座標を取り替える
		double theta=-arg((point<double>){0,1},v);
		v=rot(v,theta);
		rep(i,n) Z[i].p=rot(Z[i].p,theta);

		sort(Z,Z+n);

		double ans=INF;
		// S_u : 上方向に動くロボットの集合
		// S_d : 下方向に動くロボットの集合
		// double をそのまま比較しているけど、誤差が出るような演算はしていないので大丈夫
		set< pair<double,int> > S_u,S_d;
		for(int i=0,j=0;i<n;i++){
			// x 方向に離れすぎてロボット i とぶつかれなくなったロボットを削除
			while(j<i && Z[i].p.x-Z[j].p.x>2*r){
				if(Z[j].dir==1) S_u.erase(make_pair(Z[j].p.y,j));
				else            S_d.erase(make_pair(Z[j].p.y,j));
				j++;
			}

			set< pair<double,int> >::iterator it;
			// ロボット i は上方向に動く
			if(Z[i].dir==1){
				if(!S_d.empty()){
					it=S_d.lower_bound(make_pair(Z[i].p.y,-1));
					if(it!=S_d.end()){
						int k=it->second;
						ans=min(ans,f(Z[i],Z[k],r,v_abs));
					}
				}
			}
			// ロボット i は下方向に動く
			else{
				if(!S_u.empty()){
					it=S_u.lower_bound(make_pair(Z[i].p.y,-1));
					if(it!=S_u.begin()){
						--it;
						int k=it->second;
						ans=min(ans,f(Z[i],Z[k],r,v_abs));
					}
				}
			}

			if(Z[i].dir==1) S_u.insert(make_pair(Z[i].p.y,i));
			else            S_d.insert(make_pair(Z[i].p.y,i));
		}

		if(ans>INF/2) puts("SAFE");
		else          printf("%.9f\n",ans);
	}

	return 0;
}
accuracy: 1
time_ms: 550
memory_kb: 2848
final_rank: 1