pub trait Zero: PartialEq + Sized {
    fn zero() -> Self;
    #[inline]
    fn is_zero(&self) -> bool {
        self == &Self::zero()
    }
}
pub trait One: PartialEq + Sized {
    fn one() -> Self;
    #[inline]
    fn is_one(&self) -> bool {
        self == &Self::one()
    }
}
macro_rules !zero_one_impls {($({$Trait :ident $method :ident $($t :ty ) *,$e :expr } ) *) =>{$($(impl $Trait for $t {#[inline ] fn $method () ->Self {$e } } ) *) *} ;}
zero_one_impls !({Zero zero u8 u16 u32 u64 usize i8 i16 i32 i64 isize u128 i128 ,0 } {Zero zero f32 f64 ,0. } {One one u8 u16 u32 u64 usize i8 i16 i32 i64 isize u128 i128 ,1 } {One one f32 f64 ,1. } ) ;
pub trait IterScan: Sized {
    type Output;
    fn scan<'a, I: Iterator<Item = &'a str>>(iter: &mut I) -> Option<Self::Output>;
}
pub trait MarkedIterScan: Sized {
    type Output;
    fn mscan<'a, I: Iterator<Item = &'a str>>(self, iter: &mut I) -> Option<Self::Output>;
}
#[derive(Clone, Debug)]
pub struct Scanner<'a> {
    iter: std::str::SplitAsciiWhitespace<'a>,
}
mod scanner_impls {
    use super::*;
    impl<'a> Scanner<'a> {
        #[inline]
        pub fn new(s: &'a str) -> Self {
            let iter = s.split_ascii_whitespace();
            Self { iter }
        }
        #[inline]
        pub fn scan<T: IterScan>(&mut self) -> <T as IterScan>::Output {
            <T as IterScan>::scan(&mut self.iter).expect("scan error")
        }
        #[inline]
        pub fn mscan<T: MarkedIterScan>(&mut self, marker: T) -> <T as MarkedIterScan>::Output {
            marker.mscan(&mut self.iter).expect("scan error")
        }
        #[inline]
        pub fn scan_vec<T: IterScan>(&mut self, size: usize) -> Vec<<T as IterScan>::Output> {
            (0..size)
                .map(|_| <T as IterScan>::scan(&mut self.iter).expect("scan error"))
                .collect()
        }
        #[inline]
        pub fn iter<'b, T: IterScan>(&'b mut self) -> ScannerIter<'a, 'b, T> {
            ScannerIter {
                inner: self,
                _marker: std::marker::PhantomData,
            }
        }
    }
    macro_rules !iter_scan_impls {($($t :ty ) *) =>{$(impl IterScan for $t {type Output =Self ;#[inline ] fn scan <'a ,I :Iterator <Item =&'a str >>(iter :&mut I ) ->Option <Self >{iter .next () ?.parse ::<$t >() .ok () } } ) *} ;}
    iter_scan_impls !(char u8 u16 u32 u64 usize i8 i16 i32 i64 isize f32 f64 u128 i128 String ) ;
    macro_rules !iter_scan_tuple_impl {($($T :ident ) *) =>{impl <$($T :IterScan ) ,*>IterScan for ($($T ,) *) {type Output =($(<$T as IterScan >::Output ,) *) ;#[inline ] fn scan <'a ,It :Iterator <Item =&'a str >>(_iter :&mut It ) ->Option <Self ::Output >{Some (($(<$T as IterScan >::scan (_iter ) ?,) *) ) } } } ;}
    iter_scan_tuple_impl!();
    iter_scan_tuple_impl!(A);
    iter_scan_tuple_impl !(A B ) ;
    iter_scan_tuple_impl !(A B C ) ;
    iter_scan_tuple_impl !(A B C D ) ;
    iter_scan_tuple_impl !(A B C D E ) ;
    iter_scan_tuple_impl !(A B C D E F ) ;
    iter_scan_tuple_impl !(A B C D E F G ) ;
    iter_scan_tuple_impl !(A B C D E F G H ) ;
    iter_scan_tuple_impl !(A B C D E F G H I ) ;
    iter_scan_tuple_impl !(A B C D E F G H I J ) ;
    iter_scan_tuple_impl !(A B C D E F G H I J K ) ;
    pub struct ScannerIter<'a, 'b, T> {
        inner: &'b mut Scanner<'a>,
        _marker: std::marker::PhantomData<fn() -> T>,
    }
    impl<'a, 'b, T: IterScan> Iterator for ScannerIter<'a, 'b, T> {
        type Item = <T as IterScan>::Output;
        #[inline]
        fn next(&mut self) -> Option<Self::Item> {
            <T as IterScan>::scan(&mut self.inner.iter)
        }
    }
}
pub mod marker {
    use super::*;
    use std::{iter::FromIterator, marker::PhantomData};
    #[derive(Debug, Copy, Clone)]
    pub struct Usize1;
    impl IterScan for Usize1 {
        type Output = usize;
        #[inline]
        fn scan<'a, I: Iterator<Item = &'a str>>(iter: &mut I) -> Option<Self::Output> {
            Some(<usize as IterScan>::scan(iter)?.checked_sub(1)?)
        }
    }
    #[derive(Debug, Copy, Clone)]
    pub struct Chars;
    impl IterScan for Chars {
        type Output = Vec<char>;
        #[inline]
        fn scan<'a, I: Iterator<Item = &'a str>>(iter: &mut I) -> Option<Self::Output> {
            Some(iter.next()?.chars().collect())
        }
    }
    #[derive(Debug, Copy, Clone)]
    pub struct CharsWithBase(pub char);
    impl MarkedIterScan for CharsWithBase {
        type Output = Vec<usize>;
        #[inline]
        fn mscan<'a, I: Iterator<Item = &'a str>>(self, iter: &mut I) -> Option<Self::Output> {
            Some(
                iter.next()?
                    .chars()
                    .map(|c| (c as u8 - self.0 as u8) as usize)
                    .collect(),
            )
        }
    }
    #[derive(Debug, Copy, Clone)]
    pub struct Collect<T: IterScan, B: FromIterator<<T as IterScan>::Output>> {
        size: usize,
        _marker: PhantomData<fn() -> (T, B)>,
    }
    impl<T: IterScan, B: FromIterator<<T as IterScan>::Output>> Collect<T, B> {
        pub fn new(size: usize) -> Self {
            Self {
                size,
                _marker: PhantomData,
            }
        }
    }
    impl<T: IterScan, B: FromIterator<<T as IterScan>::Output>> MarkedIterScan for Collect<T, B> {
        type Output = B;
        #[inline]
        fn mscan<'a, I: Iterator<Item = &'a str>>(self, iter: &mut I) -> Option<Self::Output> {
            Some(
                (0..self.size)
                    .map(|_| <T as IterScan>::scan(iter).expect("scan error"))
                    .collect::<B>(),
            )
        }
    }
}
#[macro_export]
macro_rules !min {($e :expr ) =>{$e } ;($e :expr ,$($es :expr ) ,+) =>{std ::cmp ::min ($e ,min !($($es ) ,+) ) } ;}
#[macro_export]
macro_rules !chmin {($dst :expr ,$($src :expr ) ,+) =>{{let x =std ::cmp ::min ($dst ,min !($($src ) ,+) ) ;$dst =x ;} } ;}
#[macro_export]
macro_rules !max {($e :expr ) =>{$e } ;($e :expr ,$($es :expr ) ,+) =>{std ::cmp ::max ($e ,max !($($es ) ,+) ) } ;}
#[macro_export]
macro_rules !chmax {($dst :expr ,$($src :expr ) ,+) =>{{let x =std ::cmp ::max ($dst ,max !($($src ) ,+) ) ;$dst =x ;} } ;}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct GridGraph {
    height: usize,
    width: usize,
}
impl GridGraph {
    pub fn new(height: usize, width: usize) -> Self {
        Self { height, width }
    }
    pub fn adjacency4(&self, x: usize, y: usize) -> Adjacent4<'_> {
        Adjacent4 {
            grid: self,
            x,
            y,
            state: 0,
        }
    }
    pub fn adjacency8(&self, x: usize, y: usize) -> Adjacent8<'_> {
        Adjacent8 {
            grid: self,
            x,
            y,
            state: 0,
        }
    }
}
#[derive(Debug)]
pub struct Adjacent4<'a> {
    grid: &'a GridGraph,
    x: usize,
    y: usize,
    state: usize,
}
impl<'a> Iterator for Adjacent4<'a> {
    type Item = (usize, usize);
    fn next(&mut self) -> Option<Self::Item> {
        const D: [(usize, usize); 4] = [(1, 0), (0, 1), (!0, 0), (0, !0)];
        for &(dx, dy) in D[self.state..].into_iter() {
            self.state += 1;
            let nx = self.x.wrapping_add(dx);
            let ny = self.y.wrapping_add(dy);
            if nx < self.grid.height && ny < self.grid.width {
                return Some((nx, ny));
            }
        }
        None
    }
}
#[derive(Debug)]
pub struct Adjacent8<'a> {
    grid: &'a GridGraph,
    x: usize,
    y: usize,
    state: usize,
}
impl<'a> Iterator for Adjacent8<'a> {
    type Item = (usize, usize);
    fn next(&mut self) -> Option<Self::Item> {
        const D: [(usize, usize); 8] = [
            (1, 0),
            (1, 1),
            (0, 1),
            (!0, 1),
            (!0, 0),
            (!0, !0),
            (0, !0),
            (1, !0),
        ];
        for &(dx, dy) in D[self.state..].into_iter() {
            self.state += 1;
            let nx = self.x.wrapping_add(dx);
            let ny = self.y.wrapping_add(dy);
            if nx < self.grid.height && ny < self.grid.width {
                return Some((nx, ny));
            }
        }
        None
    }
}
#[derive(Debug, Clone)]
pub struct RevEdge {
    pub to: usize,
    pub rev: usize,
    pub cap: u64,
}
impl RevEdge {
    pub fn new(to: usize, rev: usize, cap: u64) -> Self {
        Self { to, rev, cap }
    }
}
#[derive(Debug)]
pub struct Dinic {
    pub graph: Vec<Vec<RevEdge>>,
    iter: Vec<usize>,
    level: Vec<usize>,
}
impl Dinic {
    pub fn new(n: usize) -> Self {
        Self {
            graph: vec![vec![]; n],
            iter: vec![],
            level: vec![],
        }
    }
    pub fn add_edge(&mut self, from: usize, to: usize, cap: u64) {
        let e1 = RevEdge::new(to, self.graph[to].len(), cap);
        let e2 = RevEdge::new(from, self.graph[from].len(), 0);
        self.graph[from].push(e1);
        self.graph[to].push(e2);
    }
    fn bfs(&mut self, s: usize) {
        self.level = vec![std::usize::MAX; self.graph.len()];
        let mut deq = std::collections::VecDeque::new();
        self.level[s] = 0;
        deq.push_back(s);
        while let Some(u) = deq.pop_front() {
            for e in &self.graph[u] {
                if e.cap > 0 && self.level[e.to] == std::usize::MAX {
                    self.level[e.to] = self.level[u] + 1;
                    deq.push_back(e.to);
                }
            }
        }
    }
    fn dfs(&mut self, u: usize, t: usize, f: u64) -> u64 {
        if u == t {
            return f;
        }
        for i in self.iter[u]..self.graph[u].len() {
            self.iter[u] = i;
            let RevEdge { to, rev, cap } = self.graph[u][i];
            if cap > 0 && self.level[u] < self.level[to] {
                let d = self.dfs(to, t, std::cmp::min(f, cap));
                if d > 0 {
                    self.graph[u][i].cap -= d;
                    self.graph[to][rev].cap += d;
                    return d;
                }
            }
        }
        0
    }
    pub fn maximum_flow(&mut self, s: usize, t: usize) -> u64 {
        let mut flow = 0;
        loop {
            self.bfs(s);
            if self.level[t] == std::usize::MAX {
                return flow;
            }
            self.iter = vec![0; self.graph.len()];
            loop {
                let f = self.dfs(s, t, std::u64::MAX);
                if f == 0 {
                    break;
                }
                flow += f;
            }
        }
    }
}
fn main() {
    #![allow(unused_imports, unused_macros)]
    use std::io::{stdin, stdout, BufWriter, Read as _, Write as _};
    let mut _in_buf = Vec::new();
    stdin().read_to_end(&mut _in_buf).expect("io error");
    let _in_buf = unsafe { String::from_utf8_unchecked(_in_buf) };
    let mut scanner = Scanner::new(&_in_buf);
    macro_rules !scan {() =>{scan !(usize ) } ;(($($t :tt ) ,*) ) =>{($(scan !($t ) ) ,*) } ;([$t :ty ;$len :expr ] ) =>{scanner .scan_vec ::<$t >($len ) } ;([$t :tt ;$len :expr ] ) =>{(0 ..$len ) .map (|_ |scan !($t ) ) .collect ::<Vec <_ >>() } ;([$t :ty ] ) =>{scanner .iter ::<$t >() } ;({$e :expr } ) =>{scanner .mscan ($e ) } ;($t :ty ) =>{scanner .scan ::<$t >() } ;}
    let _out = stdout();
    let mut _out = BufWriter::new(_out.lock());
    macro_rules !print {($($arg :tt ) *) =>(::std ::write !(_out ,$($arg ) *) .expect ("io error" ) ) }
    macro_rules !println {($($arg :tt ) *) =>(::std ::writeln !(_out ,$($arg ) *) .expect ("io error" ) ) }
    macro_rules! echo {
        ($iter :expr ) => {
            echo!($iter, '\n')
        };
        ($iter :expr ,$sep :expr ) => {
            let mut iter = $iter.into_iter();
            if let Some(item) = iter.next() {
                print!("{}", item);
            }
            for item in iter {
                print!("{}{}", $sep, item);
            }
            println!();
        };
    }
    let n = scan!();
    let m = scan!();
    let mut s = scan!([marker::Chars; n]);
    let gg = GridGraph::new(n, m);
    let mut dinic = Dinic::new(n * m + 2);
    for x in 0..n {
        for y in 0..m {
            if s[x][y] == '.' && (x + y) % 2 == 0 {
                dinic.add_edge(n * m, x * m + y, 1);
                for (nx, ny) in gg.adjacency4(x, y) {
                    if s[nx][ny] == '.' {
                        dinic.add_edge(x * m + y, nx * m + ny, 1);
                    }
                }
            } else {
                dinic.add_edge(x * m + y, n * m + 1, 1);
            }
        }
    }
    let max = dinic.maximum_flow(n * m, n * m + 1);
    println!("{}", max);
    for x in 0..n {
        for y in 0..m {
            if s[x][y] == '.' && (x + y) % 2 == 0 {
                for (nx, ny) in dinic.graph[x * m + y]
                    .iter()
                    .filter(|e| e.cap == 0 && e.to < n * m)
                    .map(|e| (e.to / m, e.to % m))
                {
                    if y == ny {
                        if x < nx {
                            s[x][y] = 'v';
                            s[nx][y] = '^';
                        } else {
                            s[x][y] = '^';
                            s[nx][y] = 'v';
                        }
                    } else {
                        if y < ny {
                            s[x][y] = '>';
                            s[x][ny] = '<';
                        } else {
                            s[x][y] = '<';
                            s[x][ny] = '>';
                        }
                    }
                }
            }
        }
    }
    for s in s {
        println!("{}", s.into_iter().collect::<String>());
    }
}

Measured memory_bytes: 4888