#include <bits/stdc++.h>

using namespace std;
using int64 = long long;

constexpr int DEBUG = 0;

struct FlowEdge {
  const int from, to;
  const int64 cap;  // Capacity
  FlowEdge(int from, int to, int64 cap) : from(from), to(to), cap(cap) {}
};

tuple<int64, vector<int64>> MaxFlow(int n, const vector<FlowEdge>& edges, int s, int t) {
  int m = edges.size();
  
  vector<vector<tuple<FlowEdge, int>>> graph(n);
  vector<int64> res_caps(2 * m);  // Edge ID to residual capacity
  vector<int> to_reverse(2 * m, -1);  // Edge ID to the reverse edge ID
  for (int i = 0; i < m; i++) {
    const auto& edge = edges[i];
    graph[edge.from].push_back(make_tuple(edge, i));
    graph[edge.to].push_back(
        make_tuple(FlowEdge(edge.to, edge.from, 0), m + i));
    res_caps[i] = edge.cap;
    to_reverse[i] = m + i;
    to_reverse[m + i] = i;
  }

  int64 total = 0;
  deque<int> q;
  vector<int> prevs(n, -1);
  vector<int> used_edges(n, -1);
  while (true) {
    q.clear();
    fill(prevs.begin(), prevs.end(), -1);
    fill(used_edges.begin(), used_edges.end(), -1);

    q.push_back(s);
    while (!q.empty()) {
      int v = q.front();
      q.pop_front();
      for (const auto& edge_tuple : graph[v]) {
        FlowEdge edge = get<0>(edge_tuple);
        int edge_id = get<1>(edge_tuple);
        if (res_caps[edge_id] == 0 || prevs[edge.to] >= 0) continue;
        prevs[edge.to] = v;
        used_edges[edge.to] = edge_id;
        q.push_back(edge.to);
      }
    }

    if (prevs[t] == -1) return {total, res_caps};

    int cur = t;
    vector<int> path;
    while (cur != s) {
      path.push_back(used_edges[cur]);
      cur = prevs[cur];
    }

    int64 inc = INT64_MAX;
    for (int edge_id : path) {
      inc = min(inc, res_caps[edge_id]);
    }

    total += inc;

    for (int edge_id : path) {
      res_caps[edge_id] -= inc;
      res_caps[to_reverse[edge_id]] += inc;
    }
  }
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);

  int h, w;
  cin >> h >> w;
  vector<string> rows(h);
  for (int y = 0; y < h; y++) {
    cin >> rows[y];
  }

  int s = h * w;
  int t = h * w + 1;
  vector<FlowEdge> edges;
  for (int y = 0; y < h; y++) {
    for (int x = 0; x < w; x++) {
      if (rows[y][x] == '#') continue;
      int v = y * w + x;
      if ((y + x) % 2 == 0) {
        edges.push_back({s, v, 1});
      } else {
        edges.push_back({v, t, 1});
      }

      if ((y + x) % 2 == 0) {
        if (x > 0 && rows[y][x - 1] == '.') {
          edges.push_back({v, v - 1, 1});
        }
        if (x < w - 1 && rows[y][x + 1] == '.') {
          edges.push_back({v, v + 1, 1});
        }
        if (y > 0 && rows[y - 1][x] == '.') {
          edges.push_back({v, v - w, 1});
        }
        if (y < h - 1 && rows[y + 1][x] == '.') {
          edges.push_back({v, v + w, 1});
        }
      }
    }
  }

  auto [max_flow, res_caps] = MaxFlow(h * w + 2, edges, s, t);
  cout << max_flow << endl;

  vector<string> output = rows;
  for (int i = 0; i < edges.size(); i++) {
    int v1 = edges[i].from;
    int v2 = edges[i].to;
    if (v1 == s || v2 == t) continue;
    if (res_caps[i] == 1) continue;
    int x1 = v1 % w;
    int y1 = v1 / w;
    int x2 = v2 % w;
    int y2 = v2 / w;
    // cout << y1 << " " << x1 << " " << y2 << " " << x2 << endl;
    if (x1 == x2) {
      output[min(y1, y2)][x1] = 'v';
      output[max(y1, y2)][x1] = '^';
    } else {
      output[y1][min(x1, x2)] = '>';
      output[y1][max(x1, x2)] = '<';
    }
  }

  for (int y = 0; y < h; y++) {
    cout << output[y] << "\n";
  }
}

Measured memory_bytes: 7336