Buggy python submission (verdict: Runtime Error):
# thanks: https://atcoder.jp/contests/practice2/submissions/16784996

#!usr/bin/env python3
from collections import defaultdict, deque, Counter, OrderedDict
from bisect import bisect_left, bisect_right
from functools import reduce, lru_cache
from heapq import heappush, heappop, heapify

import itertools
import math, fractions
import sys, copy

def L(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline().rstrip())
def SL(): return list(sys.stdin.readline().rstrip())
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LS(): return [list(x) for x in sys.stdin.readline().split()]
def R(n): return [sys.stdin.readline().strip() for _ in range(n)]
def LR(n): return [L() for _ in range(n)]
def IR(n): return [I() for _ in range(n)]
def LIR(n): return [LI() for _ in range(n)]
def LIR1(n): return [LI1() for _ in range(n)]
def SLR(n): return [SL() for _ in range(n)]
def LSR(n): return [LS() for _ in range(n)]

def perm(n, r): return math.factorial(n) // math.factorial(r)
def comb(n, r): return math.factorial(n) // (math.factorial(r) * math.factorial(n-r))

def make_list(n, *args, default=0): return [make_list(*args, default=default) for _ in range(n)] if args else [default for _ in range(n)]

dire = [[1, 0], [0, 1], [-1, 0], [0, -1]]
dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]
alphabets = "abcdefghijklmnopqrstuvwxyz"
ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
MOD = 1000000007
INF = float("inf")

sys.setrecursionlimit(1000000)

class MaxFlowGraph:
    def __init__(self, N):
        self.N = N
        self.graph = [[] for _ in range(N)]
        self.paths = []
        self.capacities = [dict() for _ in range(N)]

    def add_edge(self, v, w, cap=1):
        self.graph[v].append(w)
        self.graph[w].append(v)
        self.capacities[v][w] = cap
        self.capacities[w][v] = 0

    def dinic_bfs(self, s, t):
        self.level = [-1] * self.N
        q = deque([s])
        self.level[s] = 0
        while q:
            v = q.popleft()
            for w, cap in self.capacities[v].items():
                if cap and self.level[w] == -1:
                    self.level[w] = self.level[v] + 1
                    if w == t: return True
                    q.append(w)
        return False

    def dinic_dfs(self, s, t, limit):
        st = [t]
        while st:
            v = st[-1]
            if v == s: break
            #vから行ける全ての頂点について
            while self.it[v] < len(self.graph[v]):
                # w -> v の cap
                w = self.graph[v][self.it[v]]
                cap = self.capacities[w][v]
                if cap and self.level[w] != -1 and self.level[v] > self.level[w]:
                    st.append(w)
                    break
                self.it[v] += 1
            else:
                st.pop()
                self.level[v] = self.N
        else: return 0

        flow = min(limit, min(self.capacities[st[i + 1]][st[i]] for i in range(len(st) - 2)))
        for i in range(len(st) - 1):
            self.capacities[st[i]][st[i+1]] += flow
            self.capacities[st[i+1]][st[i]] -= flow
        self.paths.append(st)
        return flow

    def dinic_flow(self, s, t, flow_limit=18446744073709551615):
        flow = 0
        while flow < flow_limit and self.dinic_bfs(s, t):
            self.it = [0]*self.N
            while flow < flow_limit:
                f = self.dinic_dfs(s, t, flow_limit - flow)
                if not f: break
                flow += f
        return flow

    def min_cut(self, s):
        visited = [False]*self.N
        q = [s]
        while q:
            v = q.pop()
            visited[v] = True
            for w, cap in self.capacities[v].items():
                if cap and not visited[w]:
                    q.append(w)

def main():
    N, M = LI()
    S = SLR(N)
    g = MaxFlowGraph(N*M+2)

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

    # 偶数はblack, 奇数はwhiteとして市松模様に塗る、二部グラフの最大マッチング問題
    # s -> black -> white -> t の辺を張って最大流を求める
    for y in range(N):
        for x in range(M):
            # black
            if (y+x) % 2 == 0:
                g.add_edge(s, M*y+x)
                for dy, dx in [[1, 0], [0, 1]]:
                    ny, nx = y + dy, x + dx
                    if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
                        g.add_edge(M*y+x, M*ny+nx)
            # white
            else:
                g.add_edge(M*y+x, t)
                for dy, dx in [[1, 0], [0, 1]]:
                    ny, nx = y + dy, x + dx
                    if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
                        g.add_edge(M*ny+nx, M*y+x)

    print(g.dinic_flow(s, t))
    print(paths)
    for path in g.paths:
        u, v = path[1:-1]
        uy, ux = divmod(u, M)
        vy, vx = divmod(v, M)
        if uy - 1 == vy:
            S[uy][ux], S[vy][vx] = "^", "v"
        elif uy + 1 == vy:
            S[uy][ux], S[vy][vx] = "v", "^"
        elif ux + 1 == vx:
            S[uy][ux], S[vy][vx] = ">", "<"
        elif ux - 1 == vx:
            S[uy][ux], S[vy][vx] = "<", ">"
    for p in S:
        print("".join(p))

    for p in S:
        print("".join(p))



if __name__ == '__main__':
    main()

Fixed submission:
# thanks: https://atcoder.jp/contests/practice2/submissions/16784996

#!usr/bin/env python3
from collections import defaultdict, deque, Counter, OrderedDict
from bisect import bisect_left, bisect_right
from functools import reduce, lru_cache
from heapq import heappush, heappop, heapify

import itertools
import math, fractions
import sys, copy

def L(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline().rstrip())
def SL(): return list(sys.stdin.readline().rstrip())
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LS(): return [list(x) for x in sys.stdin.readline().split()]
def R(n): return [sys.stdin.readline().strip() for _ in range(n)]
def LR(n): return [L() for _ in range(n)]
def IR(n): return [I() for _ in range(n)]
def LIR(n): return [LI() for _ in range(n)]
def LIR1(n): return [LI1() for _ in range(n)]
def SLR(n): return [SL() for _ in range(n)]
def LSR(n): return [LS() for _ in range(n)]

def perm(n, r): return math.factorial(n) // math.factorial(r)
def comb(n, r): return math.factorial(n) // (math.factorial(r) * math.factorial(n-r))

def make_list(n, *args, default=0): return [make_list(*args, default=default) for _ in range(n)] if args else [default for _ in range(n)]

dire = [[1, 0], [0, 1], [-1, 0], [0, -1]]
dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]
alphabets = "abcdefghijklmnopqrstuvwxyz"
ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
MOD = 1000000007
INF = float("inf")

sys.setrecursionlimit(1000000)

class MaxFlowGraph:
    def __init__(self, N):
        self.N = N
        self.graph = [[] for _ in range(N)]
        self.capacities = [dict() for _ in range(N)]

    def add_edge(self, v, w, cap):
        self.graph[v].append(w)
        self.graph[w].append(v)
        self.capacities[v][w] = cap
        self.capacities[w][v] = 0

    def bfs(self, s, t):
        self.level = [-1] * self.N
        q = [s]
        self.level[s] = 0
        while q:
            nq = []
            for v in q:
                for w, cap in self.capacities[v].items():
                    if cap and self.level[w] == -1:
                        self.level[w] = self.level[v] + 1
                        if w == t:
                            return True
                        nq.append(w)
            q = nq
        return False

    def dfs(self, s, t, up):
        st = [t]
        while st:
            v = st[-1]
            if v == s:
                flow = up
                for i in range(len(st) - 2):
                    if flow > self.capacities[st[i + 1]][st[i]]:
                        flow = self.capacities[st[i+1]][st[i]]
                for i in range(len(st) - 1):
                    self.capacities[st[i]][st[i+1]] += flow
                    self.capacities[st[i+1]][st[i]] -= flow
                return flow
            while self.it[v] < len(self.graph[v]):
                w = self.graph[v][self.it[v]]
                cap = self.capacities[w][v]
                if cap and self.level[w] != -1:
                    if self.level[v] > self.level[w]:
                        st.append(w)
                        break
                self.it[v] += 1
            else:
                st.pop()
                self.level[v] = self.N
        return 0

    def flow(self, s, t, flow_limit=18446744073709551615):
        flow = 0
        while flow < flow_limit and self.bfs(s, t):
            self.it = [0]*self.N
            while flow < flow_limit:
                f = self.dfs(s, t, flow_limit - flow)
                if not f:
                    break
                flow += f
        return flow

    def min_cut(self, s):
        visited = [False]*self.N
        q = [s]
        while q:
            v = q.pop()
            visited[v] = True
            for w, cap in self.capacities[v].items():
                if cap and not visited[w]:
                    q.append(w)

def main():
    N, M = LI()
    S = SLR(N)
    g = MaxFlowGraph(N*M+2)

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

    # 偶数はblack, 奇数はwhiteとして市松模様に塗る、二部グラフの最大マッチング問題
    # s -> black -> white -> t の辺を張って最大流を求める
    # for y in range(N):
    #     for x in range(M):
    #         # black
    #         if (y+x) % 2 == 0:
    #             g.add_edge(s, M*y+x)
    #             for dy, dx in [[1, 0], [0, 1]]:
    #                 ny, nx = y + dy, x + dx
    #                 if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
    #                     g.add_edge(M*y+x, M*ny+nx)
    #         # white
    #         else:
    #             g.add_edge(M*y+x, t)
    #             for dy, dx in [[1, 0], [0, 1]]:
    #                 ny, nx = y + dy, x + dx
    #                 if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
    #                     g.add_edge(M*ny+nx, M*y+x)


    for i in range(N):
        for j in range(M):
            if (i+j) % 2 == 0:
                g.add_edge(s, M*i+j, 1)
            else:
                g.add_edge(M*i+j, t, 1)

    for i in range(N):
        for j in range(M):
            if j+1 < M and S[i][j] == "." and S[i][j+1] == ".":
                u, v = M*i+j, M*i+j+1
                if (i+j) % 2 == 1:
                    u, v = v, u
                g.add_edge(u, v, 1)
            if i+1 < N and S[i][j] == "." and S[i + 1][j] == ".":
                u, v = M*i+j, M*(i+1)+j
                if (i+j) % 2 == 1:
                    u, v = v, u
                g.add_edge(u, v, 1)

    print(g.flow(s, t))

    # blackから考える
    for u in range(N*M+2):
        uy, ux = divmod(u, M)
        if (uy + ux) % 2 == 1: continue

        for v, cap in g.capacities[u].items():
            if cap != 0: continue
            vy, vx = divmod(v, M)
            if u != s and u != t and v != s and v != t:
                if uy - 1 == vy:
                    S[uy][ux], S[vy][vx] = "^", "v"
                elif uy + 1 == vy:
                    S[uy][ux], S[vy][vx] = "v", "^"
                elif ux + 1 == vx:
                    S[uy][ux], S[vy][vx] = ">", "<"
                elif ux - 1 == vx:
                    S[uy][ux], S[vy][vx] = "<", ">"

    for p in S:
        print("".join(p))



if __name__ == '__main__':
    main()

Buggy python submission (verdict: Runtime Error):
# thanks: https://atcoder.jp/contests/practice2/submissions/16784996

#!usr/bin/env python3
from collections import defaultdict, deque, Counter, OrderedDict
from bisect import bisect_left, bisect_right
from functools import reduce, lru_cache
from heapq import heappush, heappop, heapify

import itertools
import math, fractions
import sys, copy

def L(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline().rstrip())
def SL(): return list(sys.stdin.readline().rstrip())
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LS(): return [list(x) for x in sys.stdin.readline().split()]
def R(n): return [sys.stdin.readline().strip() for _ in range(n)]
def LR(n): return [L() for _ in range(n)]
def IR(n): return [I() for _ in range(n)]
def LIR(n): return [LI() for _ in range(n)]
def LIR1(n): return [LI1() for _ in range(n)]
def SLR(n): return [SL() for _ in range(n)]
def LSR(n): return [LS() for _ in range(n)]

def perm(n, r): return math.factorial(n) // math.factorial(r)
def comb(n, r): return math.factorial(n) // (math.factorial(r) * math.factorial(n-r))

def make_list(n, *args, default=0): return [make_list(*args, default=default) for _ in range(n)] if args else [default for _ in range(n)]

dire = [[1, 0], [0, 1], [-1, 0], [0, -1]]
dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]
alphabets = "abcdefghijklmnopqrstuvwxyz"
ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
MOD = 1000000007
INF = float("inf")

sys.setrecursionlimit(1000000)

class MaxFlowGraph:
    def __init__(self, N):
        self.N = N
        self.graph = [[] for _ in range(N)]
        self.paths = []
        self.capacities = [dict() for _ in range(N)]

    def add_edge(self, v, w, cap=1):
        self.graph[v].append(w)
        self.graph[w].append(v)
        self.capacities[v][w] = cap
        self.capacities[w][v] = 0

    def dinic_bfs(self, s, t):
        self.level = [-1] * self.N
        q = deque([s])
        self.level[s] = 0
        while q:
            v = q.popleft()
            for w, cap in self.capacities[v].items():
                if cap and self.level[w] == -1:
                    self.level[w] = self.level[v] + 1
                    if w == t: return True
                    q.append(w)
        return False

    def dinic_dfs(self, s, t, limit):
        st = [t]
        while st:
            v = st[-1]
            if v == s: break
            #vから行ける全ての頂点について
            while self.it[v] < len(self.graph[v]):
                # w -> v の cap
                w = self.graph[v][self.it[v]]
                cap = self.capacities[w][v]
                if cap and self.level[w] != -1 and self.level[v] > self.level[w]:
                    st.append(w)
                    break
                self.it[v] += 1
            else:
                st.pop()
                self.level[v] = self.N
        else: return 0

        flow = min(limit, min(self.capacities[st[i + 1]][st[i]] for i in range(len(st) - 2)))
        for i in range(len(st) - 1):
            self.capacities[st[i]][st[i+1]] += flow
            self.capacities[st[i+1]][st[i]] -= flow
        self.paths.append(st)
        return flow

    def dinic_flow(self, s, t, flow_limit=18446744073709551615):
        flow = 0
        while flow < flow_limit and self.dinic_bfs(s, t):
            self.it = [0]*self.N
            while flow < flow_limit:
                f = self.dinic_dfs(s, t, flow_limit - flow)
                if not f: break
                flow += f
        return flow

    def min_cut(self, s):
        visited = [False]*self.N
        q = [s]
        while q:
            v = q.pop()
            visited[v] = True
            for w, cap in self.capacities[v].items():
                if cap and not visited[w]:
                    q.append(w)

def main():
    N, M = LI()
    S = SLR(N)
    g = MaxFlowGraph(N*M+2)

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

    # 偶数はblack, 奇数はwhiteとして市松模様に塗る、二部グラフの最大マッチング問題
    # s -> black -> white -> t の辺を張って最大流を求める
    for y in range(N):
        for x in range(M):
            # black
            if (y+x) % 2 == 0:
                g.add_edge(s, M*y+x)
                for dy, dx in [[1, 0], [0, 1]]:
                    ny, nx = y + dy, x + dx
                    if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
                        g.add_edge(M*y+x, M*ny+nx)
            # white
            else:
                g.add_edge(M*y+x, t)
                for dy, dx in [[1, 0], [0, 1]]:
                    ny, nx = y + dy, x + dx
                    if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
                        g.add_edge(M*ny+nx, M*y+x)

    print(g.dinic_flow(s, t))

    for path in g.paths:
        u, v = path[1:-1]
        uy, ux = divmod(u, M)
        vy, vx = divmod(v, M)
        if uy - 1 == vy:
            S[uy][ux], S[vy][vx] = "^", "v"
        elif uy + 1 == vy:
            S[uy][ux], S[vy][vx] = "v", "^"
        elif ux + 1 == vx:
            S[uy][ux], S[vy][vx] = ">", "<"
        elif ux - 1 == vx:
            S[uy][ux], S[vy][vx] = "<", ">"
    for p in S:
        print("".join(p))




if __name__ == '__main__':
    main()

Fixed submission:
# thanks: https://atcoder.jp/contests/practice2/submissions/16784996

#!usr/bin/env python3
from collections import defaultdict, deque, Counter, OrderedDict
from bisect import bisect_left, bisect_right
from functools import reduce, lru_cache
from heapq import heappush, heappop, heapify

import itertools
import math, fractions
import sys, copy

def L(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline().rstrip())
def SL(): return list(sys.stdin.readline().rstrip())
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LS(): return [list(x) for x in sys.stdin.readline().split()]
def R(n): return [sys.stdin.readline().strip() for _ in range(n)]
def LR(n): return [L() for _ in range(n)]
def IR(n): return [I() for _ in range(n)]
def LIR(n): return [LI() for _ in range(n)]
def LIR1(n): return [LI1() for _ in range(n)]
def SLR(n): return [SL() for _ in range(n)]
def LSR(n): return [LS() for _ in range(n)]

def perm(n, r): return math.factorial(n) // math.factorial(r)
def comb(n, r): return math.factorial(n) // (math.factorial(r) * math.factorial(n-r))

def make_list(n, *args, default=0): return [make_list(*args, default=default) for _ in range(n)] if args else [default for _ in range(n)]

dire = [[1, 0], [0, 1], [-1, 0], [0, -1]]
dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]
alphabets = "abcdefghijklmnopqrstuvwxyz"
ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
MOD = 1000000007
INF = float("inf")

sys.setrecursionlimit(1000000)

class MaxFlowGraph:
    def __init__(self, N):
        self.N = N
        self.graph = [[] for _ in range(N)]
        self.capacities = [dict() for _ in range(N)]

    def add_edge(self, v, w, cap):
        self.graph[v].append(w)
        self.graph[w].append(v)
        self.capacities[v][w] = cap
        self.capacities[w][v] = 0

    def bfs(self, s, t):
        self.level = [-1] * self.N
        q = [s]
        self.level[s] = 0
        while q:
            nq = []
            for v in q:
                for w, cap in self.capacities[v].items():
                    if cap and self.level[w] == -1:
                        self.level[w] = self.level[v] + 1
                        if w == t:
                            return True
                        nq.append(w)
            q = nq
        return False

    def dfs(self, s, t, up):
        st = [t]
        while st:
            v = st[-1]
            if v == s:
                flow = up
                for i in range(len(st) - 2):
                    if flow > self.capacities[st[i + 1]][st[i]]:
                        flow = self.capacities[st[i+1]][st[i]]
                for i in range(len(st) - 1):
                    self.capacities[st[i]][st[i+1]] += flow
                    self.capacities[st[i+1]][st[i]] -= flow
                return flow
            while self.it[v] < len(self.graph[v]):
                w = self.graph[v][self.it[v]]
                cap = self.capacities[w][v]
                if cap and self.level[w] != -1:
                    if self.level[v] > self.level[w]:
                        st.append(w)
                        break
                self.it[v] += 1
            else:
                st.pop()
                self.level[v] = self.N
        return 0

    def flow(self, s, t, flow_limit=18446744073709551615):
        flow = 0
        while flow < flow_limit and self.bfs(s, t):
            self.it = [0]*self.N
            while flow < flow_limit:
                f = self.dfs(s, t, flow_limit - flow)
                if not f:
                    break
                flow += f
        return flow

    def min_cut(self, s):
        visited = [False]*self.N
        q = [s]
        while q:
            v = q.pop()
            visited[v] = True
            for w, cap in self.capacities[v].items():
                if cap and not visited[w]:
                    q.append(w)

def main():
    N, M = LI()
    S = SLR(N)
    g = MaxFlowGraph(N*M+2)

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

    # 偶数はblack, 奇数はwhiteとして市松模様に塗る、二部グラフの最大マッチング問題
    # s -> black -> white -> t の辺を張って最大流を求める
    # for y in range(N):
    #     for x in range(M):
    #         # black
    #         if (y+x) % 2 == 0:
    #             g.add_edge(s, M*y+x)
    #             for dy, dx in [[1, 0], [0, 1]]:
    #                 ny, nx = y + dy, x + dx
    #                 if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
    #                     g.add_edge(M*y+x, M*ny+nx)
    #         # white
    #         else:
    #             g.add_edge(M*y+x, t)
    #             for dy, dx in [[1, 0], [0, 1]]:
    #                 ny, nx = y + dy, x + dx
    #                 if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
    #                     g.add_edge(M*ny+nx, M*y+x)


    for i in range(N):
        for j in range(M):
            if (i+j) % 2 == 0:
                g.add_edge(s, M*i+j, 1)
            else:
                g.add_edge(M*i+j, t, 1)

    for i in range(N):
        for j in range(M):
            if j+1 < M and S[i][j] == "." and S[i][j+1] == ".":
                u, v = M*i+j, M*i+j+1
                if (i+j) % 2 == 1:
                    u, v = v, u
                g.add_edge(u, v, 1)
            if i+1 < N and S[i][j] == "." and S[i + 1][j] == ".":
                u, v = M*i+j, M*(i+1)+j
                if (i+j) % 2 == 1:
                    u, v = v, u
                g.add_edge(u, v, 1)

    print(g.flow(s, t))

    # blackから考える
    for u in range(N*M+2):
        uy, ux = divmod(u, M)
        if (uy + ux) % 2 == 1: continue

        for v, cap in g.capacities[u].items():
            if cap != 0: continue
            vy, vx = divmod(v, M)
            if u != s and u != t and v != s and v != t:
                if uy - 1 == vy:
                    S[uy][ux], S[vy][vx] = "^", "v"
                elif uy + 1 == vy:
                    S[uy][ux], S[vy][vx] = "v", "^"
                elif ux + 1 == vx:
                    S[uy][ux], S[vy][vx] = ">", "<"
                elif ux - 1 == vx:
                    S[uy][ux], S[vy][vx] = "<", ">"

    for p in S:
        print("".join(p))



if __name__ == '__main__':
    main()

Buggy python submission (verdict: Time Limit Exceeded):
# thanks: https://atcoder.jp/contests/practice2/submissions/16784996

#!usr/bin/env python3
from collections import defaultdict, deque, Counter, OrderedDict
from bisect import bisect_left, bisect_right
from functools import reduce, lru_cache
from heapq import heappush, heappop, heapify

import itertools
import math, fractions
import sys, copy

def L(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline().rstrip())
def SL(): return list(sys.stdin.readline().rstrip())
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LS(): return [list(x) for x in sys.stdin.readline().split()]
def R(n): return [sys.stdin.readline().strip() for _ in range(n)]
def LR(n): return [L() for _ in range(n)]
def IR(n): return [I() for _ in range(n)]
def LIR(n): return [LI() for _ in range(n)]
def LIR1(n): return [LI1() for _ in range(n)]
def SLR(n): return [SL() for _ in range(n)]
def LSR(n): return [LS() for _ in range(n)]

def perm(n, r): return math.factorial(n) // math.factorial(r)
def comb(n, r): return math.factorial(n) // (math.factorial(r) * math.factorial(n-r))

def make_list(n, *args, default=0): return [make_list(*args, default=default) for _ in range(n)] if args else [default for _ in range(n)]

dire = [[1, 0], [0, 1], [-1, 0], [0, -1]]
dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]
alphabets = "abcdefghijklmnopqrstuvwxyz"
ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
MOD = 1000000007
INF = float("inf")

sys.setrecursionlimit(1000000)

class MaxFlowGraph:
    def __init__(self, N):
        self.N = N
        self.graph = [[] for _ in range(N)]
        self.capacities = [defaultdict(int) for _ in range(N)]

    def add_edge(self, v, w, cap=1):
        self.graph[v].append(w)
        self.graph[w].append(v)
        self.capacities[v][w] = cap

    def bfs(self, s, t):
        self.level = [-1] * self.N
        q = [s]
        self.level[s] = 0
        while q:
            nq = []
            for v in q:
                for w, cap in self.capacities[v].items():
                    if cap and self.level[w] == -1:
                        self.level[w] = self.level[v] + 1
                        if w == t:
                            return True
                        nq.append(w)
            q = nq
        return False

    def lookup_flow_path(self, s, t):
        frm = [-2] * self.N
        q = deque([s])
        frm[s] = -1

        while q:
            v = q.popleft()
            for u, cap in self.capacities[v].items():
                if cap and frm[u] == -2:
                    frm[u] = v
                    if u == t: break
                    q.append(u)
            else: continue
            break
        else:
            return []
        # [t, u, v, ..., s]
        ret = [t]
        now = t
        while now != s:
            now = frm[now]
            ret.append(now)
        return ret


    def dfs(self, s, t, up):
        st = [t]
        while st:
            v = st[-1]
            if v == s:
                flow = min(up ,min(self.capacities[st[i + 1]][st[i]] for i in range(len(st) - 2)))
                for i in range(len(st) - 1):
                    self.capacities[st[i]][st[i+1]] += flow
                    self.capacities[st[i+1]][st[i]] -= flow
                return flow
            #vから行ける全ての頂点について
            while self.it[v] < len(self.graph[v]):
                # w -> v の cap
                w = self.graph[v][self.it[v]]
                cap = self.capacities[w][v]
                if cap and self.level[w] != -1:
                    if self.level[v] > self.level[w]:
                        st.append(w)
                        break
                self.it[v] += 1
            else:
                st.pop()
                self.level[v] = self.N
        return 0

    def flow(self, s, t, flow_limit=18446744073709551615):
        current_flow = 0
        path = self.lookup_flow_path(s, t)
        while current_flow < flow_limit and path:
            min_flow = min(flow_limit - current_flow, min(self.capacities[path[i + 1]][path[i]] for i in range(len(path) - 2)))
            for i in range(len(path) - 1):
                self.capacities[path[i]][path[i+1]] += min_flow
                self.capacities[path[i+1]][path[i]] -= min_flow
            current_flow += min_flow
            path = self.lookup_flow_path(s, t)
        return current_flow

        # while flow < flow_limit and self.bfs(s, t):
        #     self.it = [0]*self.N
        #     while flow < flow_limit:
        #         f = self.dfs(s, t, flow_limit - flow)
        #         if not f: break
        #         flow += f
        # return flow

    def min_cut(self, s):
        visited = [False]*self.N
        q = [s]
        while q:
            v = q.pop()
            visited[v] = True
            for w, cap in self.capacities[v].items():
                if cap and not visited[w]:
                    q.append(w)

def main():
    N, M = LI()
    S = SLR(N)
    g = MaxFlowGraph(N*M+2)

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

    # 偶数はblack, 奇数はwhiteとして市松模様に塗る、二部グラフの最大マッチング問題
    # s -> black -> white -> t の辺を張って最大流を求める
    for y in range(N):
        for x in range(M):
            # black
            if (y+x) % 2 == 0:
                g.add_edge(s, M*y+x)
                for dy, dx in [[1, 0], [0, 1]]:
                    ny, nx = y + dy, x + dx
                    if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
                        g.add_edge(M*y+x, M*ny+nx)
            # white
            else:
                g.add_edge(M*y+x, t)
                for dy, dx in [[1, 0], [0, 1]]:
                    ny, nx = y + dy, x + dx
                    if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
                        g.add_edge(M*ny+nx, M*y+x)

    print(g.flow(s, t))

    # blackから考える
    for u in range(N*M+2):
        uy, ux = divmod(u, M)
        if (uy + ux) % 2 == 1: continue

        for v, cap in g.capacities[u].items():
            if cap != 0: continue
            vy, vx = divmod(v, M)
            if u != s and u != t and v != s and v != t:
                if uy - 1 == vy:
                    S[uy][ux], S[vy][vx] = "^", "v"
                elif uy + 1 == vy:
                    S[uy][ux], S[vy][vx] = "v", "^"
                elif ux + 1 == vx:
                    S[uy][ux], S[vy][vx] = ">", "<"
                elif ux - 1 == vx:
                    S[uy][ux], S[vy][vx] = "<", ">"

    for p in S:
        print("".join(p))



if __name__ == '__main__':
    main()

Fixed submission:
# thanks: https://atcoder.jp/contests/practice2/submissions/16784996

#!usr/bin/env python3
from collections import defaultdict, deque, Counter, OrderedDict
from bisect import bisect_left, bisect_right
from functools import reduce, lru_cache
from heapq import heappush, heappop, heapify

import itertools
import math, fractions
import sys, copy

def L(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline().rstrip())
def SL(): return list(sys.stdin.readline().rstrip())
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LS(): return [list(x) for x in sys.stdin.readline().split()]
def R(n): return [sys.stdin.readline().strip() for _ in range(n)]
def LR(n): return [L() for _ in range(n)]
def IR(n): return [I() for _ in range(n)]
def LIR(n): return [LI() for _ in range(n)]
def LIR1(n): return [LI1() for _ in range(n)]
def SLR(n): return [SL() for _ in range(n)]
def LSR(n): return [LS() for _ in range(n)]

def perm(n, r): return math.factorial(n) // math.factorial(r)
def comb(n, r): return math.factorial(n) // (math.factorial(r) * math.factorial(n-r))

def make_list(n, *args, default=0): return [make_list(*args, default=default) for _ in range(n)] if args else [default for _ in range(n)]

dire = [[1, 0], [0, 1], [-1, 0], [0, -1]]
dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]
alphabets = "abcdefghijklmnopqrstuvwxyz"
ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
MOD = 1000000007
INF = float("inf")

sys.setrecursionlimit(1000000)

class MaxFlowGraph:
    def __init__(self, N):
        self.N = N
        self.graph = [[] for _ in range(N)]
        self.capacities = [dict() for _ in range(N)]

    def add_edge(self, v, w, cap):
        self.graph[v].append(w)
        self.graph[w].append(v)
        self.capacities[v][w] = cap
        self.capacities[w][v] = 0

    def bfs(self, s, t):
        self.level = [-1] * self.N
        q = [s]
        self.level[s] = 0
        while q:
            nq = []
            for v in q:
                for w, cap in self.capacities[v].items():
                    if cap and self.level[w] == -1:
                        self.level[w] = self.level[v] + 1
                        if w == t:
                            return True
                        nq.append(w)
            q = nq
        return False

    def dfs(self, s, t, up):
        st = [t]
        while st:
            v = st[-1]
            if v == s:
                flow = up
                for i in range(len(st) - 2):
                    if flow > self.capacities[st[i + 1]][st[i]]:
                        flow = self.capacities[st[i+1]][st[i]]
                for i in range(len(st) - 1):
                    self.capacities[st[i]][st[i+1]] += flow
                    self.capacities[st[i+1]][st[i]] -= flow
                return flow
            while self.it[v] < len(self.graph[v]):
                w = self.graph[v][self.it[v]]
                cap = self.capacities[w][v]
                if cap and self.level[w] != -1:
                    if self.level[v] > self.level[w]:
                        st.append(w)
                        break
                self.it[v] += 1
            else:
                st.pop()
                self.level[v] = self.N
        return 0

    def flow(self, s, t, flow_limit=18446744073709551615):
        flow = 0
        while flow < flow_limit and self.bfs(s, t):
            self.it = [0]*self.N
            while flow < flow_limit:
                f = self.dfs(s, t, flow_limit - flow)
                if not f:
                    break
                flow += f
        return flow

    def min_cut(self, s):
        visited = [False]*self.N
        q = [s]
        while q:
            v = q.pop()
            visited[v] = True
            for w, cap in self.capacities[v].items():
                if cap and not visited[w]:
                    q.append(w)

def main():
    N, M = LI()
    S = SLR(N)
    g = MaxFlowGraph(N*M+2)

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

    # 偶数はblack, 奇数はwhiteとして市松模様に塗る、二部グラフの最大マッチング問題
    # s -> black -> white -> t の辺を張って最大流を求める
    # for y in range(N):
    #     for x in range(M):
    #         # black
    #         if (y+x) % 2 == 0:
    #             g.add_edge(s, M*y+x)
    #             for dy, dx in [[1, 0], [0, 1]]:
    #                 ny, nx = y + dy, x + dx
    #                 if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
    #                     g.add_edge(M*y+x, M*ny+nx)
    #         # white
    #         else:
    #             g.add_edge(M*y+x, t)
    #             for dy, dx in [[1, 0], [0, 1]]:
    #                 ny, nx = y + dy, x + dx
    #                 if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
    #                     g.add_edge(M*ny+nx, M*y+x)


    for i in range(N):
        for j in range(M):
            if (i+j) % 2 == 0:
                g.add_edge(s, M*i+j, 1)
            else:
                g.add_edge(M*i+j, t, 1)

    for i in range(N):
        for j in range(M):
            if j+1 < M and S[i][j] == "." and S[i][j+1] == ".":
                u, v = M*i+j, M*i+j+1
                if (i+j) % 2 == 1:
                    u, v = v, u
                g.add_edge(u, v, 1)
            if i+1 < N and S[i][j] == "." and S[i + 1][j] == ".":
                u, v = M*i+j, M*(i+1)+j
                if (i+j) % 2 == 1:
                    u, v = v, u
                g.add_edge(u, v, 1)

    print(g.flow(s, t))

    # blackから考える
    for u in range(N*M+2):
        uy, ux = divmod(u, M)
        if (uy + ux) % 2 == 1: continue

        for v, cap in g.capacities[u].items():
            if cap != 0: continue
            vy, vx = divmod(v, M)
            if u != s and u != t and v != s and v != t:
                if uy - 1 == vy:
                    S[uy][ux], S[vy][vx] = "^", "v"
                elif uy + 1 == vy:
                    S[uy][ux], S[vy][vx] = "v", "^"
                elif ux + 1 == vx:
                    S[uy][ux], S[vy][vx] = ">", "<"
                elif ux - 1 == vx:
                    S[uy][ux], S[vy][vx] = "<", ">"

    for p in S:
        print("".join(p))



if __name__ == '__main__':
    main()

Buggy python submission (verdict: Runtime Error):
# thanks: https://atcoder.jp/contests/practice2/submissions/16784996

#!usr/bin/env python3
from collections import defaultdict, deque, Counter, OrderedDict
from bisect import bisect_left, bisect_right
from functools import reduce, lru_cache
from heapq import heappush, heappop, heapify

import itertools
import math, fractions
import sys, copy

def L(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline().rstrip())
def SL(): return list(sys.stdin.readline().rstrip())
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LS(): return [list(x) for x in sys.stdin.readline().split()]
def R(n): return [sys.stdin.readline().strip() for _ in range(n)]
def LR(n): return [L() for _ in range(n)]
def IR(n): return [I() for _ in range(n)]
def LIR(n): return [LI() for _ in range(n)]
def LIR1(n): return [LI1() for _ in range(n)]
def SLR(n): return [SL() for _ in range(n)]
def LSR(n): return [LS() for _ in range(n)]

def perm(n, r): return math.factorial(n) // math.factorial(r)
def comb(n, r): return math.factorial(n) // (math.factorial(r) * math.factorial(n-r))

def make_list(n, *args, default=0): return [make_list(*args, default=default) for _ in range(n)] if args else [default for _ in range(n)]

dire = [[1, 0], [0, 1], [-1, 0], [0, -1]]
dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]
alphabets = "abcdefghijklmnopqrstuvwxyz"
ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
MOD = 1000000007
INF = float("inf")

sys.setrecursionlimit(1000000)

class MaxFlowGraph:
    def __init__(self, N):
        self.N = N
        self.graph = [[] for _ in range(N)]
        self.capacities = [dict() for _ in range(N)]

    def add_edge(self, v, w, cap):
        self.graph[v].append(w)
        self.graph[w].append(v)
        self.capacities[v][w] = cap
        self.capacities[w][v] = 0

    def bfs(self, s, t):
        self.level = [-1] * self.N
        q = [s]
        self.level[s] = 0
        while q:
            nq = []
            for v in q:
                for w, cap in self.capacities[v].items():
                    if cap and self.level[w] == -1:
                        self.level[w] = self.level[v] + 1
                        if w == t:
                            return True
                        nq.append(w)
            q = nq
        return False

    def dfs(self, s, t, up):
        st = [t]
        while st:
            v = st[-1]
            if v == s:
                flow = up
                for i in range(len(st) - 2):
                    if flow > self.capacities[st[i + 1]][st[i]]:
                        flow = self.capacities[st[i+1]][st[i]]
                for i in range(len(st) - 1):
                    self.capacities[st[i]][st[i+1]] += flow
                    self.capacities[st[i+1]][st[i]] -= flow
                return flow
            while self.it[v] < len(self.graph[v]):
                w = self.graph[v][self.it[v]]
                cap = self.capacities[w][v]
                if cap and self.level[w] != -1:
                    if self.level[v] > self.level[w]:
                        st.append(w)
                        break
                self.it[v] += 1
            else:
                st.pop()
                self.level[v] = self.N
        return 0

    def flow(self, s, t, flow_limit=18446744073709551615):
        flow = 0
        while flow < flow_limit and self.bfs(s, t):
            self.it = [0]*self.N
            while flow < flow_limit:
                f = self.dfs(s, t, flow_limit - flow)
                if not f:
                    break
                flow += f
        return flow

    def min_cut(self, s):
        visited = [False]*self.N
        q = [s]
        while q:
            v = q.pop()
            visited[v] = True
            for w, cap in self.capacities[v].items():
                if cap and not visited[w]:
                    q.append(w)

def main():
    N, M = LI()
    S = SLR(N)
    g = MaxFlowGraph(N*M+2)

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

    # 偶数はblack, 奇数はwhiteとして市松模様に塗る、二部グラフの最大マッチング問題
    # s -> black -> white -> t の辺を張って最大流を求める
    # for y in range(N):
    #     for x in range(M):
    #         # black
    #         if (y+x) % 2 == 0:
    #             g.add_edge(s, M*y+x)
    #             for dy, dx in [[1, 0], [0, 1]]:
    #                 ny, nx = y + dy, x + dx
    #                 if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
    #                     g.add_edge(M*y+x, M*ny+nx)
    #         # white
    #         else:
    #             g.add_edge(M*y+x, t)
    #             for dy, dx in [[1, 0], [0, 1]]:
    #                 ny, nx = y + dy, x + dx
    #                 if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
    #                     g.add_edge(M*ny+nx, M*y+x)


    for i in range(N):
        for j in range(M):
            if (i+j) % 2 == 0:
                g.add_edge(s, M*i+j, 1)
            else:
                g.add_edge(M*i+j, t, 1)

    for i in range(N):
        for j in range(M):
            if j+1 < M and S[i][j] == "." and S[i][j+1] == ".":
                u, v = M*i+j, M*i+j+1
                if (i+j) % 2 == 1:
                    u, v = v, u
                g.add_edge(u, v, 1)
            if i+1 < N and S[i][j] == "." and S[i + 1][j] == ".":
                u, v = M*i+j, M*(i+1)+j
                if (i+j) % 2 == 1:
                    u, v = v, u
                g.add_edge(u, v, 1)

    print(g.flow(s, t))

    # blackから考える
    for u in range(0, N*M+1, 2):
        uy, ux = divmod(u, M)

        for v, cap in g.capacities[u].items():
            if cap != 0: continue
            vy, vx = divmod(v, M)
            if v != s and v != t:
                if uy - 1 == vy:
                    S[uy][ux], S[vy][vx] = "^", "v"
                elif uy + 1 == vy:
                    S[uy][ux], S[vy][vx] = "v", "^"
                elif ux + 1 == vx:
                    S[uy][ux], S[vy][vx] = ">", "<"
                elif ux - 1 == vx:
                    S[uy][ux], S[vy][vx] = "<", ">"

    for p in S:
        print("".join(p))



if __name__ == '__main__':
    main()

Fixed submission:
# thanks: https://atcoder.jp/contests/practice2/submissions/16784996

#!usr/bin/env python3
from collections import defaultdict, deque, Counter, OrderedDict
from bisect import bisect_left, bisect_right
from functools import reduce, lru_cache
from heapq import heappush, heappop, heapify

import itertools
import math, fractions
import sys, copy

def L(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline().rstrip())
def SL(): return list(sys.stdin.readline().rstrip())
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LS(): return [list(x) for x in sys.stdin.readline().split()]
def R(n): return [sys.stdin.readline().strip() for _ in range(n)]
def LR(n): return [L() for _ in range(n)]
def IR(n): return [I() for _ in range(n)]
def LIR(n): return [LI() for _ in range(n)]
def LIR1(n): return [LI1() for _ in range(n)]
def SLR(n): return [SL() for _ in range(n)]
def LSR(n): return [LS() for _ in range(n)]

def perm(n, r): return math.factorial(n) // math.factorial(r)
def comb(n, r): return math.factorial(n) // (math.factorial(r) * math.factorial(n-r))

def make_list(n, *args, default=0): return [make_list(*args, default=default) for _ in range(n)] if args else [default for _ in range(n)]

dire = [[1, 0], [0, 1], [-1, 0], [0, -1]]
dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]
alphabets = "abcdefghijklmnopqrstuvwxyz"
ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
MOD = 1000000007
INF = float("inf")

sys.setrecursionlimit(1000000)

class MaxFlowGraph:
    def __init__(self, N):
        self.N = N
        self.graph = [[] for _ in range(N)]
        self.capacities = [dict() for _ in range(N)]

    def add_edge(self, v, w, cap):
        self.graph[v].append(w)
        self.graph[w].append(v)
        self.capacities[v][w] = cap
        self.capacities[w][v] = 0

    def bfs(self, s, t):
        self.level = [-1] * self.N
        q = [s]
        self.level[s] = 0
        while q:
            nq = []
            for v in q:
                for w, cap in self.capacities[v].items():
                    if cap and self.level[w] == -1:
                        self.level[w] = self.level[v] + 1
                        if w == t:
                            return True
                        nq.append(w)
            q = nq
        return False

    def dfs(self, s, t, up):
        st = [t]
        while st:
            v = st[-1]
            if v == s:
                flow = up
                for i in range(len(st) - 2):
                    if flow > self.capacities[st[i + 1]][st[i]]:
                        flow = self.capacities[st[i+1]][st[i]]
                for i in range(len(st) - 1):
                    self.capacities[st[i]][st[i+1]] += flow
                    self.capacities[st[i+1]][st[i]] -= flow
                return flow
            while self.it[v] < len(self.graph[v]):
                w = self.graph[v][self.it[v]]
                cap = self.capacities[w][v]
                if cap and self.level[w] != -1:
                    if self.level[v] > self.level[w]:
                        st.append(w)
                        break
                self.it[v] += 1
            else:
                st.pop()
                self.level[v] = self.N
        return 0

    def flow(self, s, t, flow_limit=18446744073709551615):
        flow = 0
        while flow < flow_limit and self.bfs(s, t):
            self.it = [0]*self.N
            while flow < flow_limit:
                f = self.dfs(s, t, flow_limit - flow)
                if not f:
                    break
                flow += f
        return flow

    def min_cut(self, s):
        visited = [False]*self.N
        q = [s]
        while q:
            v = q.pop()
            visited[v] = True
            for w, cap in self.capacities[v].items():
                if cap and not visited[w]:
                    q.append(w)

def main():
    N, M = LI()
    S = SLR(N)
    g = MaxFlowGraph(N*M+2)

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

    # 偶数はblack, 奇数はwhiteとして市松模様に塗る、二部グラフの最大マッチング問題
    # s -> black -> white -> t の辺を張って最大流を求める
    # for y in range(N):
    #     for x in range(M):
    #         # black
    #         if (y+x) % 2 == 0:
    #             g.add_edge(s, M*y+x)
    #             for dy, dx in [[1, 0], [0, 1]]:
    #                 ny, nx = y + dy, x + dx
    #                 if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
    #                     g.add_edge(M*y+x, M*ny+nx)
    #         # white
    #         else:
    #             g.add_edge(M*y+x, t)
    #             for dy, dx in [[1, 0], [0, 1]]:
    #                 ny, nx = y + dy, x + dx
    #                 if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
    #                     g.add_edge(M*ny+nx, M*y+x)


    for i in range(N):
        for j in range(M):
            if (i+j) % 2 == 0:
                g.add_edge(s, M*i+j, 1)
            else:
                g.add_edge(M*i+j, t, 1)

    for i in range(N):
        for j in range(M):
            if j+1 < M and S[i][j] == "." and S[i][j+1] == ".":
                u, v = M*i+j, M*i+j+1
                if (i+j) % 2 == 1:
                    u, v = v, u
                g.add_edge(u, v, 1)
            if i+1 < N and S[i][j] == "." and S[i + 1][j] == ".":
                u, v = M*i+j, M*(i+1)+j
                if (i+j) % 2 == 1:
                    u, v = v, u
                g.add_edge(u, v, 1)

    print(g.flow(s, t))

    # blackから考える
    for u in range(N*M+2):
        uy, ux = divmod(u, M)
        if (uy + ux) % 2 == 1: continue

        for v, cap in g.capacities[u].items():
            if cap != 0: continue
            vy, vx = divmod(v, M)
            if u != s and u != t and v != s and v != t:
                if uy - 1 == vy:
                    S[uy][ux], S[vy][vx] = "^", "v"
                elif uy + 1 == vy:
                    S[uy][ux], S[vy][vx] = "v", "^"
                elif ux + 1 == vx:
                    S[uy][ux], S[vy][vx] = ">", "<"
                elif ux - 1 == vx:
                    S[uy][ux], S[vy][vx] = "<", ">"

    for p in S:
        print("".join(p))



if __name__ == '__main__':
    main()

Buggy python submission (verdict: Runtime Error):
#!usr/bin/env python3
from collections import defaultdict, deque, Counter, OrderedDict
from bisect import bisect_left, bisect_right
from functools import reduce, lru_cache
from heapq import heappush, heappop, heapify

import itertools
import math, fractions
import sys, copy

def L(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline().rstrip())
def SL(): return list(sys.stdin.readline().rstrip())
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LS(): return [list(x) for x in sys.stdin.readline().split()]
def R(n): return [sys.stdin.readline().strip() for _ in range(n)]
def LR(n): return [L() for _ in range(n)]
def IR(n): return [I() for _ in range(n)]
def LIR(n): return [LI() for _ in range(n)]
def LIR1(n): return [LI1() for _ in range(n)]
def SLR(n): return [SL() for _ in range(n)]
def LSR(n): return [LS() for _ in range(n)]

def perm(n, r): return math.factorial(n) // math.factorial(r)
def comb(n, r): return math.factorial(n) // (math.factorial(r) * math.factorial(n-r))

def make_list(n, *args, default=0): return [make_list(*args, default=default) for _ in range(n)] if args else [default for _ in range(n)]

dire = [[1, 0], [0, 1], [-1, 0], [0, -1]]
dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]
alphabets = "abcdefghijklmnopqrstuvwxyz"
ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
MOD = 1000000007
INF = float("inf")

sys.setrecursionlimit(1000000)

class MaxFlowGraph:
    def __init__(self, N):
        self.N = N
        self.graph = [[] for _ in range(N)]
        self.capacities = [defaultdict(int) for _ in range(N)]

    def add_edge(self, v, w, cap=1):
        self.graph[v].append(w)
        self.graph[w].append(v)
        self.capacities[v][w] = cap
        self.capacities[w][v] = 0

    def flow(self, s, t, flow_limit=0XFFFFFFFFFFFFFFF):
        flow = 0
        while flow < flow_limit and self.bfs(s, t):
            while flow < flow_limit:
                f = self.dfs(s, t, flow_limit - flow)
                if not f:
                    break
                flow += f
        return flow

    def bfs(self, s, t):
        self.level = [-1] * self.N
        q = [s]
        self.level[s] = 0
        while q:
            nq = []
            for v in q:
                for w, cap in self.capacities[v].items():
                    if cap and self.level[w] == -1:
                        self.level[w] = self.level[v] + 1
                        if w == t:
                            return True
                        nq.append(w)
            q = nq
        return False

    def dfs(self, s, t, up):
        self.it = [0]*self.N
        st = [t]
        while st:
            v = st[-1]
            if v == s:
                flow = up
                for i in range(len(st) - 2):
                    if flow > self.capacities[st[i + 1]][st[i]]:
                        flow = self.capacities[st[i+1]][st[i]]
                for i in range(len(st) - 1):
                    self.capacities[st[i]][st[i+1]] += flow
                    self.capacities[st[i+1]][st[i]] -= flow
                return flow
            while self.it[v] < len(self.graph[v]):
                w = self.graph[v][self.it[v]]
                cap = self.capacities[w][v]
                if cap and self.level[w] != -1:
                    if self.level[v] > self.level[w]:
                        st.append(w)
                        break
                self.it[v] += 1
            else:
                st.pop()
                self.level[v] = self.N
        return 0

    def min_cut(self, s):
        visited = [False]*self.N
        q = [s]
        while q:
            v = q.pop()
            visited[v] = True
            for w, cap in self.capacities[v].items():
                if cap and not visited[w]:
                    q.append(w)


def main():
    N, M = LI()
    S = SLR(N)
    g = MaxFlowGraph(N*M+2)

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

    # 偶数はblack, 奇数はwhiteとして市松模様に塗る、二部グラフの最大マッチング問題
    # s -> black -> white -> t の辺を張って最大流を求める
    for y in range(N):
        for x in range(M):
            # black
            if (y+x) % 2 == 0:
                g.add_edge(s, M*y+x)
                for dy, dx in [[1, 0], [0, 1]]:
                    ny, nx = y + dy, x + dx
                    if 0 <= ny < M and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
                        g.add_edge(M*y+x, M*ny+nx)
            # white
            else:
                g.add_edge(M*y+x, t)
                for dy, dx in [[1, 0], [0, 1]]:
                    ny, nx = y + dy, x + dx
                    if 0 <= ny < M and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
                        g.add_edge(M*ny+nx, M*y+x)

    print(g.flow(s, t))

    # blackから考える
    for u in range(0, N*M, 2):
        uy, ux = divmod(u, M)

        for v, cap in g.capacities[u].items():
            if cap != 0: continue
            vy, vx = divmod(v, M)
            if v != s and v != t:
                if uy - 1 == vy:
                    S[uy][ux], S[vy][vx] = "^", "v"
                elif uy + 1 == vy:
                    S[uy][ux], S[vy][vx] = "v", "^"
                elif ux + 1 == vx:
                    S[uy][ux], S[vy][vx] = ">", "<"
                elif ux - 1 == vx:
                    S[uy][ux], S[vy][vx] = "<", ">"

    for p in S:
        print("".join(p))



if __name__ == '__main__':
    main()

Fixed submission:
# thanks: https://atcoder.jp/contests/practice2/submissions/16784996

#!usr/bin/env python3
from collections import defaultdict, deque, Counter, OrderedDict
from bisect import bisect_left, bisect_right
from functools import reduce, lru_cache
from heapq import heappush, heappop, heapify

import itertools
import math, fractions
import sys, copy

def L(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline().rstrip())
def SL(): return list(sys.stdin.readline().rstrip())
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LS(): return [list(x) for x in sys.stdin.readline().split()]
def R(n): return [sys.stdin.readline().strip() for _ in range(n)]
def LR(n): return [L() for _ in range(n)]
def IR(n): return [I() for _ in range(n)]
def LIR(n): return [LI() for _ in range(n)]
def LIR1(n): return [LI1() for _ in range(n)]
def SLR(n): return [SL() for _ in range(n)]
def LSR(n): return [LS() for _ in range(n)]

def perm(n, r): return math.factorial(n) // math.factorial(r)
def comb(n, r): return math.factorial(n) // (math.factorial(r) * math.factorial(n-r))

def make_list(n, *args, default=0): return [make_list(*args, default=default) for _ in range(n)] if args else [default for _ in range(n)]

dire = [[1, 0], [0, 1], [-1, 0], [0, -1]]
dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]
alphabets = "abcdefghijklmnopqrstuvwxyz"
ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
MOD = 1000000007
INF = float("inf")

sys.setrecursionlimit(1000000)

class MaxFlowGraph:
    def __init__(self, N):
        self.N = N
        self.graph = [[] for _ in range(N)]
        self.capacities = [dict() for _ in range(N)]

    def add_edge(self, v, w, cap):
        self.graph[v].append(w)
        self.graph[w].append(v)
        self.capacities[v][w] = cap
        self.capacities[w][v] = 0

    def bfs(self, s, t):
        self.level = [-1] * self.N
        q = [s]
        self.level[s] = 0
        while q:
            nq = []
            for v in q:
                for w, cap in self.capacities[v].items():
                    if cap and self.level[w] == -1:
                        self.level[w] = self.level[v] + 1
                        if w == t:
                            return True
                        nq.append(w)
            q = nq
        return False

    def dfs(self, s, t, up):
        st = [t]
        while st:
            v = st[-1]
            if v == s:
                flow = up
                for i in range(len(st) - 2):
                    if flow > self.capacities[st[i + 1]][st[i]]:
                        flow = self.capacities[st[i+1]][st[i]]
                for i in range(len(st) - 1):
                    self.capacities[st[i]][st[i+1]] += flow
                    self.capacities[st[i+1]][st[i]] -= flow
                return flow
            while self.it[v] < len(self.graph[v]):
                w = self.graph[v][self.it[v]]
                cap = self.capacities[w][v]
                if cap and self.level[w] != -1:
                    if self.level[v] > self.level[w]:
                        st.append(w)
                        break
                self.it[v] += 1
            else:
                st.pop()
                self.level[v] = self.N
        return 0

    def flow(self, s, t, flow_limit=18446744073709551615):
        flow = 0
        while flow < flow_limit and self.bfs(s, t):
            self.it = [0]*self.N
            while flow < flow_limit:
                f = self.dfs(s, t, flow_limit - flow)
                if not f:
                    break
                flow += f
        return flow

    def min_cut(self, s):
        visited = [False]*self.N
        q = [s]
        while q:
            v = q.pop()
            visited[v] = True
            for w, cap in self.capacities[v].items():
                if cap and not visited[w]:
                    q.append(w)

def main():
    N, M = LI()
    S = SLR(N)
    g = MaxFlowGraph(N*M+2)

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

    # 偶数はblack, 奇数はwhiteとして市松模様に塗る、二部グラフの最大マッチング問題
    # s -> black -> white -> t の辺を張って最大流を求める
    # for y in range(N):
    #     for x in range(M):
    #         # black
    #         if (y+x) % 2 == 0:
    #             g.add_edge(s, M*y+x)
    #             for dy, dx in [[1, 0], [0, 1]]:
    #                 ny, nx = y + dy, x + dx
    #                 if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
    #                     g.add_edge(M*y+x, M*ny+nx)
    #         # white
    #         else:
    #             g.add_edge(M*y+x, t)
    #             for dy, dx in [[1, 0], [0, 1]]:
    #                 ny, nx = y + dy, x + dx
    #                 if 0 <= ny < N and 0 <= nx < M and S[y][x] == "." and S[ny][nx] == ".":
    #                     g.add_edge(M*ny+nx, M*y+x)


    for i in range(N):
        for j in range(M):
            if (i+j) % 2 == 0:
                g.add_edge(s, M*i+j, 1)
            else:
                g.add_edge(M*i+j, t, 1)

    for i in range(N):
        for j in range(M):
            if j+1 < M and S[i][j] == "." and S[i][j+1] == ".":
                u, v = M*i+j, M*i+j+1
                if (i+j) % 2 == 1:
                    u, v = v, u
                g.add_edge(u, v, 1)
            if i+1 < N and S[i][j] == "." and S[i + 1][j] == ".":
                u, v = M*i+j, M*(i+1)+j
                if (i+j) % 2 == 1:
                    u, v = v, u
                g.add_edge(u, v, 1)

    print(g.flow(s, t))

    # blackから考える
    for u in range(N*M+2):
        uy, ux = divmod(u, M)
        if (uy + ux) % 2 == 1: continue

        for v, cap in g.capacities[u].items():
            if cap != 0: continue
            vy, vx = divmod(v, M)
            if u != s and u != t and v != s and v != t:
                if uy - 1 == vy:
                    S[uy][ux], S[vy][vx] = "^", "v"
                elif uy + 1 == vy:
                    S[uy][ux], S[vy][vx] = "v", "^"
                elif ux + 1 == vx:
                    S[uy][ux], S[vy][vx] = ">", "<"
                elif ux - 1 == vx:
                    S[uy][ux], S[vy][vx] = "<", ">"

    for p in S:
        print("".join(p))



if __name__ == '__main__':
    main()