code = r"""
# distutils: language=c++
# distutils: include_dirs=[/home/contestant/.local/lib/python3.8/site-packages/numpy/core/include, /opt/atcoder-stl]
# cython: boundscheck=False
# cython: wraparound=False
from libcpp.vector cimport vector
from libcpp cimport bool
cdef extern from "<atcoder/maxflow>" namespace "atcoder":
    cdef cppclass mf_graph[Cap]:
        mf_graph(int n)
        int add_edge(int fr, int to, Cap cap)
        Cap flow(int s, int t)
        Cap flow(int s, int t, Cap flow_limit)
        vector[bool] min_cut(int s)
        cppclass edge:
            int frm "from"
            int to
            Cap cap
            Cap flow
            edge(edge &e)
        edge get_edge(int i)
        vector[edge] edges()
        void change_edge(int i, Cap new_cap, Cap new_flow)
 
cdef class MfGraph:
    cdef mf_graph[int] *_thisptr
    def __cinit__(self, int n):
        self._thisptr = new mf_graph[int](n)
    cpdef int add_edge(self, int fr, int to, int cap):
        return self._thisptr.add_edge(fr, to, cap)
    cpdef int flow(self, int s, int t):
        return self._thisptr.flow(s, t)
    cpdef int flow_with_limit(self, int s, int t, int flow_limit):
        return self._thisptr.flow(s, t, flow_limit)
    cpdef vector[bool] min_cut(self, int s):
        return self._thisptr.min_cut(s)
 
    cpdef vector[int] get_edge(self, int i):
        cdef mf_graph[int].edge *e = new mf_graph[int].edge(self._thisptr.get_edge(i))
        cdef vector[int] *ret_e = new vector[int]()        
        ret_e.push_back(e.frm)
        ret_e.push_back(e.to)
        ret_e.push_back(e.cap)
        ret_e.push_back(e.flow)
        return ret_e[0]
 
    cpdef vector[vector[int]] edges(self):
        cdef vector[mf_graph[int].edge] es = self._thisptr.edges()
        cdef vector[vector[int]] *ret_es = new vector[vector[int]](es.size())
        for i in range(es.size()):
            ret_es.at(i).push_back(es.at(i).frm)
            ret_es.at(i).push_back(es.at(i).to)
            ret_es.at(i).push_back(es.at(i).cap)
            ret_es.at(i).push_back(es.at(i).flow)
        return ret_es[0]
    cpdef void change_edge(self, int i, int new_cap, int new_flow):
        self._thisptr.change_edge(i, new_cap, new_flow)
 
 
"""
import os,sys
if sys.argv[-1] == 'ONLINE_JUDGE':
    open('atcoder.pyx','w').write(code)
    os.system('cythonize -i -3 -b atcoder.pyx')
from atcoder import MfGraph
N,M = list(map(int,input().split()))
S = [input() for i in range(N)]

mg = MfGraph(N*M + 2)

s = N*M
t = N*M + 1

for i in range(N):
    for j in range(M):
        if S[i][j]=="#":
            continue
        v = i*M + j
        if (i+j)%2==0:
            mg.add_edge(s,v,1)
        else:
            mg.add_edge(v,t,1)

for i in range(N):
    for j in range(M):
        if (i+j)%2==1 or S[i][j]=="#":
            continue
        v0 = i*M + j
        if i>0:
            if S[i-1][j]==".":
                v1 = (i-1)*M + j
                mg.add_edge(v0,v1,1)
        if j>0:
            if S[i][j-1]==".":
                v1 = i*M + j-1
                mg.add_edge(v0,v1,1)
        if i<N-1:
            if S[i+1][j]==".":
                v1 = (i+1)*M + j
                mg.add_edge(v0,v1,1)
        if j<M-1:
            if S[i][j+1]==".":
                v1 = i*M + j+1
                mg.add_edge(v0,v1,1)

print(mg.flow(s, t))

edges = mg.edges()

T = [list(__) for __ in S]

for e in edges:
    if e[0]==s or e[1]==t or e[3]==0:
        continue
    i0 = e[0]//M
    j0 = e[0]%M
    i1 = e[1]//M
    j1 = e[1]%M
    if i0 == i1+1:
        T[i1][j1] = 'v'
        T[i0][j0] = '^'
    elif j0 == j1+1:
        T[i1][j1] = '>'
        T[i0][j0] = '<'
    elif i0 == i1-1:
        T[i1][j1] = '^'
        T[i0][j0] = 'v'
    else:
        T[i1][j1] = '<'
        T[i0][j0] = '>'

for __ in T:
    print("".join(__))

Measured memory_bytes: 19656