local read = setmetatable({}, {__index = function(t, k) local a = {} for i=1,#k do table.insert(a, '*'..string.sub(k, i, i)) end local r = io.read local u = table.unpack or unpack return function() return r(u(a)) end end})
read.N = function(N) local t={} for i=1,N do t[i]=read.n() end return t end
string.totable = function(s) local t={} local u=string.sub for i=1,#s do t[i] = u(s, i, i) end return t end
string.split = function(s) local t={} for w in string.gmatch(s, "[^%s]+") do table.insert(t, w) end return (table.unpack or unpack)(t) end
local function array(dimensi0n, default_val) assert(type(default_val) ~= 'table') local n=dimensi0n local m={}if default_val~=nil then m[1]={__index=function()return default_val end}end for i=2,n do m[i]={__index=function(p, k)local c=setmetatable({},m[i-1])rawset(p,k,c)return c end}end return setmetatable({},m[n])end
local function tostringxx(o, depth) depth = depth or 0 if depth > 10 then return "<too deep!>" end if o == _G then return "<_G>" end local indent0 = (" "):rep((depth) * 2) local indent1 = (" "):rep((depth+1) * 2) local indent2= (" "):rep((depth+2) * 2) if type(o) == 'table' then local keys = {} local types = {} for k in pairs(o) do types[type(k)] = true table.insert(keys, k) end local types_count = 0 local lasttype for k in pairs(types) do types_count = types_count + 1 lasttype = k end if types_count == 1 and (lasttype == 'string' or lasttype == 'number') then table.sort(keys) end local inside = {} for i=1,#keys do local k = keys[i] local v = o[k] if type(k) == 'string' then k = string.format('%q', k) end table.insert(inside, indent1 .. '['..tostring(k)..'] = ' .. tostringxx(v, depth + 1)) end return '{\n' .. table.concat(inside, ',\n') .. '\n' .. indent0 .. '}' else if type(o) == 'string' then o = string.format('%q', o) end return tostring(o) end end
local function richtraceback() local x = 2 while true do local info = debug.getinfo(x) if not info then break end local fname = '<' .. info.short_src .. ":" .. info.linedefined .. ">" if info.name then fname = info.name end print(info.short_src .. ":" .. info.currentline .. ": in " .. ("%q"):format(fname)) print(" LOCALS:") local p = 1 while true do local name, val = debug.getlocal(x,p) if not name then break end print(" " .. name .. ": " .. tostringxx(val, 3)) p = p + 1 end print(" UPVALUES:") for p=1,info.nups do local name, val = debug.getupvalue(info.func,p) if not name then break end print(" " .. name .. ": " .. tostringxx(val, 3)) end x = x + 1 end end
local function myassert(b) if not b then richtraceback() error("assertion failed") end end 
--
local DBG = false
local insert = table.insert
local min = math.min
local sort = table.sort
local function getkeys(t)
    local keys = {}
    for k,v in pairs(t) do
        insert(keys, k)
    end
    sort(keys)
    return keys
end

local function dbgpr(...)
    if DBG then
        io.write("[dbg]")
        print(...)
    end
end
--
local N = read.nl()
local X, Y, U = {},{},{}
local yy = {}
local xx = {}
local rr = {} -- right down
local ll = {} -- left down
local function push(tt, p, e)
    if not tt[p] then
        tt[p] = {}
    end
    insert(tt[p], e)
end
for i=1,N do
    X[i], Y[i], U[i] = read.nnl()
    U[i] = U[i]:sub(2,2)
    local e = i
    push(yy, Y[i], e)
    push(xx, X[i], e)
    local rrp = X[i] + Y[i]
    push(rr, rrp, e)
    local llp = X[i] - Y[i]
    push(ll, llp, e)
end
--richtraceback()
local INF = 10^18
local nearest_crash = INF
local function f1(name, tt, ttk, inv, fw, bk, sp)
    for _,w in ipairs(ttk) do
        local t = tt[w]
        dbgpr("on same " .. name , w)
        local inttk = {}
        for _,i in ipairs(t) do
            if U[i] == fw or U[i] == bk then
                insert(inttk, {inv[i], i, U[i]})
            end
        end
        sort(inttk, function (a1, a2)
            return a1[1] < a2[1]
        end)
        local state = 0
        local recentfwinv = -200000
        for _,ee in ipairs(inttk) do
            local i = ee[2]
            if U[i] == fw or U[i] == bk then
                dbgpr("plane #" .. i, "inv="..inv[i], "X="..X[i], "Y="..Y[i], U[i])
                if state == 0 then
                    if U[i] == fw then
                        state = 1
                        recentfwinv = inv[i]
                    end
                elseif state == 1 then
                    if U[i] == fw then
                        recentfwinv = inv[i]
                    elseif U[i] == bk then
                        local d = inv[i] - recentfwinv
                        nearest_crash = min(nearest_crash, d * sp)
                        dbgpr("crash!", i, nearest_crash)
                    end
                end
            end
        end
    end
    
end
f1('Y', yy, getkeys(yy), X, 'R', 'L', 5)
f1('X', xx, getkeys(xx), Y, 'U', 'D', 5)
local rrk = getkeys(rr)
f1('rr', rr, rrk, X, 'R', 'U', 10)
f1('rr', rr, rrk, X, 'D', 'L', 10)
local llk = getkeys(ll)
f1('ll', ll, llk, X, 'U', 'L', 10)
f1('ll', ll, llk, X, 'R', 'D', 10)

if nearest_crash == INF then
    print("SAFE")
else
    print(nearest_crash)
end
