#[allow(unused_imports)]
use {
   proconio::{fastout, input, marker::*},
   std::cmp::*,
   std::collections::*,
};

const INF: usize = 10000000000000;

#[fastout]
fn main() {
   input! {
      n: usize,
      xyu:[( usize, usize, char); n]
   }

   let mut ans: usize = INF;
   {
      let mut xr = vec![vec![]; 200001];
      let mut xl = vec![vec![]; 200001];
      let mut yu = vec![vec![]; 200001];
      let mut yd = vec![vec![]; 200001];
      for &(x, y, u) in xyu.iter() {
         match u {
            'U' => yu[x].push(y),
            'D' => yd[x].push(y),
            'R' => xr[y].push(x),
            'L' => xl[y].push(x),
            _ => unreachable!(),
         }
      }
      for i in 0..=200000 {
         if !xr.is_empty() && !xl.is_empty() {
            xl[i].sort();
            xr[i].sort();
            let mut p = 0;
            for &x1 in xr[i].iter() {
               while p < xl[i].len() && xl[i][p] < x1 {
                  p += 1;
               }
               if p == xl[i].len() {
                  break;
               }
               ans = min(ans, xl[i][p] - x1)
            }
         }
         if !yd.is_empty() && !yu.is_empty() {
            yd[i].sort();
            yu[i].sort();
            let mut p = 0;
            for &y1 in yu[i].iter() {
               while p != yd[i].len() && yd[i][p] < y1 {
                  p += 1;
               }
               if p == yd[i].len() {
                  break;
               }
               ans = min(ans, yd[i][p] - y1)
            }
         }
      }
   }

   {
      let mut xr = BTreeMap::<i64, Vec<usize>>::new();
      let mut xl = BTreeMap::<i64, Vec<usize>>::new();
      let mut yu = BTreeMap::<i64, Vec<usize>>::new();
      let mut yd = BTreeMap::<i64, Vec<usize>>::new();
      for &(x, y, u) in xyu.iter() {
         match u {
            'U' => {
               yu.entry(y as i64 - x as i64)
                  .and_modify(|v| v.push(x))
                  .or_insert(vec![x]);
            }
            'D' => {
               yd.entry(y as i64 - x as i64)
                  .and_modify(|v| v.push(x))
                  .or_insert(vec![x]);
            }
            'R' => {
               xr.entry(y as i64 - x as i64)
                  .and_modify(|v| v.push(x))
                  .or_insert(vec![x]);
            }
            'L' => {
               xl.entry(y as i64 - x as i64)
                  .and_modify(|v| v.push(x))
                  .or_insert(vec![x]);
            }
            _ => (),
         };
      }
      for i in 0..=400000 {
         let d = i as i64 - 200000;
         if yu.contains_key(&d) && xl.contains_key(&d) {
            let x = xl.get_mut(&d).unwrap();
            let y = yu.get_mut(&d).unwrap();
            x.sort();
            y.sort();
            let mut p = 0;
            for &x1 in y.iter() {
               while p != x.len() && x[p] < x1 {
                  p += 1;
               }
               if p == x.len() {
                  break;
               }
               ans = min(ans, 2 * (x[p] - x1))
            }
         }
         if yd.contains_key(&d) && xr.contains_key(&d) {
            let x = xr.get_mut(&d).unwrap();
            let y = yd.get_mut(&d).unwrap();
            x.sort();
            y.sort();
            let mut p = 0;
            for &x1 in x.iter() {
               while p != y.len() && y[p] < x1 {
                  p += 1;
               }
               if p == y.len() {
                  break;
               }
               ans = min(ans, 2 * (y[p] - x1));
            }
         }
      }
   }
   {
      let mut xr = BTreeMap::<i64, Vec<usize>>::new();
      let mut xl = BTreeMap::<i64, Vec<usize>>::new();
      let mut yu = BTreeMap::<i64, Vec<usize>>::new();
      let mut yd = BTreeMap::<i64, Vec<usize>>::new();
      for &(x, y, u) in xyu.iter() {
         match u {
            'U' => {
               yu.entry(y as i64 + x as i64)
                  .and_modify(|v| v.push(x))
                  .or_insert(vec![x]);
            }
            'D' => {
               yd.entry(y as i64 + x as i64)
                  .and_modify(|v| v.push(x))
                  .or_insert(vec![x]);
            }
            'R' => {
               xr.entry(y as i64 + x as i64)
                  .and_modify(|v| v.push(x))
                  .or_insert(vec![x]);
            }
            'L' => {
               xl.entry(y as i64 + x as i64)
                  .and_modify(|v| v.push(x))
                  .or_insert(vec![x]);
            }
            _ => (),
         };
      }
      for i in 0..=400000 {
         let d = i as i64;
         if yd.contains_key(&d) && xl.contains_key(&d) {
            let x = xl.get_mut(&d).unwrap();
            let y = yd.get_mut(&d).unwrap();
            x.sort();
            y.sort();
            let mut p = 0;
            for &x1 in y.iter() {
               while p != x.len() && x[p] < x1 {
                  p += 1;
               }
               if p == x.len() {
                  break;
               }
               ans = min(ans, 2 * (x[p] - x1));
            }
         }
         if yu.contains_key(&d) && xr.contains_key(&d) {
            let x = xr.get_mut(&d).unwrap();
            let y = yu.get_mut(&d).unwrap();
            x.sort();
            y.sort();
            let mut p = 0;
            for &x1 in x.iter() {
               while p != y.len() && y[p] < x1 {
                  p += 1;
               }
               if p == y.len() {
                  break;
               }
               ans = min(ans, 2 * (y[p] - x1));
            }
         }
      }
   }
   if ans == INF {
      println!("SAFE");
   }else {
      println!("{}", (ans as f64) / 2.0 * 10.0);
   }
}


Measured memory_bytes: 35196