#include <bits/stdc++.h>
using namespace std;
#define mp make_pair
#define pb push_back
#define x first
#define y second
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) { x < y ? x = y : T(); }
template <typename T> void chkmin(T &x, T y) { x > y ? x = y : T(); }
template <typename T> void readint(T &x) {
  x = 0;
  int f = 1;
  char c;
  for (c = getchar(); !isdigit(c); c = getchar())
    if (c == '-')
      f = -1;
  for (; isdigit(c); c = getchar())
    x = x * 10 + c - '0';
  x *= f;
}
const int MOD = 998244353;
inline int dmy(int x) { return x >= MOD ? x - MOD : x; }
inline void inc(int &x, int y) { x = dmy(x + y); }
int qmi(int x, int y) {
  int ans = 1;
  for (; y; y >>= 1, x = 1ll * x * x % MOD)
    if (y & 1)
      ans = 1ll * ans * x % MOD;
  return ans;
}
const int MAXN = 2505, MAXM = 20005, INF = 0x3f3f3f3f;

int n, m;
char a[55][55];

struct Edge {
  int v, f, c;
  Edge *next, *rev;
} pool[MAXM], *h[MAXN], *pe[MAXN];
int etot = 0;
inline void addEdge(int u, int v, int f, int c) {
  Edge *p = &pool[++etot], *q = &pool[++etot];
  p->v = v, p->f = f, p->c = c, p->next = h[u], h[u] = p, p->rev = q;
  q->v = u, q->f = 0, q->c = -c, q->next = h[v], h[v] = q, q->rev = p;
}
int src, sink;
int q[MAXM], dis[MAXN], pv[MAXN];
bool inq[MAXN];
bool spfa() {
  int front = 1, rear = 0;
  memset(dis, 0x3f, sizeof(dis));
  dis[src] = 0;
  q[++rear] = src;
  inq[src] = 1;
  while (front <= rear) {
    int u = q[front++];
    inq[u] = 0;
    for (Edge *p = h[u]; p; p = p->next)
      if (p->f && dis[p->v] > dis[u] + p->c) {
        dis[p->v] = dis[u] + p->c;
        pv[p->v] = u, pe[p->v] = p;
        if (!inq[p->v])
          q[++rear] = p->v;
      }
  }
  return dis[sink] < INF;
}
int augment() {
  int t = INF;
  for (int u = sink; u != src; u = pv[u])
    chkmin(t, pe[u]->f);
  for (int u = sink; u != src; u = pv[u])
    pe[u]->f -= t, pe[u]->rev->f += t;
  return t;
}

int main() {
#ifdef LOCAL
  freopen("code.in", "r", stdin);
  //	freopen("code.out","w",stdout);
#endif
  readint(n), readint(m);
  for (int i = 0; i < n; ++i)
    scanf("%s", a[i]);
  src = n * m, sink = n * m + 1;
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < m; ++j) {
      if (a[i][j] == '#')
        continue;
      if (a[i][j] == 'o')
        addEdge(src, i * m + j, 1, 0);
      if (i <= n - 2)
        addEdge(i * m + j, (i + 1) * m + j, INF, -1);
      if (j <= n - 2)
        addEdge(i * m + j, i * m + j + 1, INF, -1);
      addEdge(i * m + j, sink, 1, 0);
    }
  int cost = 0, t;
  while (spfa())
    t = augment(), cost += t * dis[sink];
  printf("%d\\n", -cost);
  return 0;
}

#include <bits/stdc++.h>
using namespace std;
#define mp make_pair
#define pb push_back
#define x first
#define y second
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) { x < y ? x = y : T(); }
template <typename T> void chkmin(T &x, T y) { x > y ? x = y : T(); }
template <typename T> void readint(T &x) {
  x = 0;
  int f = 1;
  char c;
  for (c = getchar(); !isdigit(c); c = getchar())
    if (c == '-')
      f = -1;
  for (; isdigit(c); c = getchar())
    x = x * 10 + c - '0';
  x *= f;
}
const int MOD = 998244353;
inline int dmy(int x) { return x >= MOD ? x - MOD : x; }
inline void inc(int &x, int y) { x = dmy(x + y); }
int qmi(int x, int y) {
  int ans = 1;
  for (; y; y >>= 1, x = 1ll * x * x % MOD)
    if (y & 1)
      ans = 1ll * ans * x % MOD;
  return ans;
}
const int MAXN = 25005, MAXM = 200005, INF = 0x3f3f3f3f;

int n, m;
char a[55][55];

struct Edge {
  int v, f, c;
  Edge *next, *rev;
} pool[MAXM], *h[MAXN], *pe[MAXN];
int etot = 0;
inline void addEdge(int u, int v, int f, int c) {
  Edge *p = &pool[++etot], *q = &pool[++etot];
  p->v = v, p->f = f, p->c = c, p->next = h[u], h[u] = p, p->rev = q;
  q->v = u, q->f = 0, q->c = -c, q->next = h[v], h[v] = q, q->rev = p;
}
int src, sink;
int q[MAXM], dis[MAXN], pv[MAXN];
bool inq[MAXN];
bool spfa() {
  int front = 1, rear = 0;
  memset(dis, 0x3f, sizeof(dis));
  dis[src] = 0;
  q[++rear] = src;
  inq[src] = 1;
  while (front <= rear) {
    int u = q[front++];
    inq[u] = 0;
    for (Edge *p = h[u]; p; p = p->next)
      if (p->f && dis[p->v] > dis[u] + p->c) {
        dis[p->v] = dis[u] + p->c;
        pv[p->v] = u, pe[p->v] = p;
        if (!inq[p->v])
          q[++rear] = p->v;
      }
  }
  return dis[sink] < INF;
}
int augment() {
  int t = INF;
  for (int u = sink; u != src; u = pv[u])
    chkmin(t, pe[u]->f);
  for (int u = sink; u != src; u = pv[u])
    pe[u]->f -= t, pe[u]->rev->f += t;
  return t;
}

int main() {
#ifdef LOCAL
  freopen("code.in", "r", stdin);
  //	freopen("code.out","w",stdout);
#endif
  readint(n), readint(m);
  for (int i = 0; i < n; ++i)
    scanf("%s", a[i]);
  src = n * m, sink = n * m + 1;
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < m; ++j) {
      if (a[i][j] == '#')
        continue;
      if (a[i][j] == 'o')
        addEdge(src, i * m + j, 1, 0);
      if (i <= n - 2)
        addEdge(i * m + j, (i + 1) * m + j, INF, -1);
      if (j <= m - 2)
        addEdge(i * m + j, i * m + j + 1, INF, -1);
      addEdge(i * m + j, sink, 1, 0);
    }
  int cost = 0, t;
  while (spfa())
    t = augment(), cost += t * dis[sink];
  printf("%d\\n", -cost);
  return 0;
}


#include <bits/stdc++.h>
// #pragma GCC optimization ("Ofast")
// #pragma GCC optimization ("unroll-loops")
using namespace std;
#define int long long
#define rep(i, n) for (int i = 0; i < n; i++)
#define REP(i, n) for (int i = 1; i < n; i++)
#define rev(i, n) for (int i = n - 1; i >= 0; i--)
#define all(v) v.begin(), v.end()
#define P pair<int, int>
#define len(s) (int)s.size()

template <class T> inline bool chmin(T &a, T b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
template <class T> inline bool chmax(T &a, T b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}
constexpr int mod = 1e9 + 7;
constexpr long long inf = 3e18;
struct PrimalDual {
  struct edge {
    int to, cap, cost, rev;
  };
  vector<vector<edge>> graph;
  void add_edge(int from, int to, int cap, int cost) {
    graph[from].push_back({to, cap, cost, len(graph[to])});
    graph[to].push_back({from, 0ll, -cost, len(graph[from]) - 1});
  }
  int minCostFlow(int s, int t, int f) {
    int V = len(graph);
    vector<int> potential(V), minCost, prevv(V, -1), preve(V, -1);
    vector<bool> used(V);
    int res = 0;
    while (f > 0) {
      minCost.assign(V, inf);
      used.assign(V, 0);
      minCost[s] = 0;
      rep(_, V - 1) {
        int mn = inf, idx;
        rep(i, V) if (!used[i] && chmin(mn, minCost[i])) idx = i;
        used[idx] = true;
        rep(i, len(graph[idx])) {
          edge &e = graph[idx][i];
          int nextcost = mn + e.cost + potential[idx] - potential[e.to];
          if (e.cap > 0 && minCost[e.to] > nextcost) {
            minCost[e.to] = nextcost;
            prevv[e.to] = idx;
            preve[e.to] = i;
          }
        }
      }
      if (minCost[t] == inf)
        return -1;
      rep(i, V) potential[i] += minCost[i];
      int addflow = f;
      for (int i = t; i != s; i = prevv[i])
        chmin(addflow, graph[prevv[i]][preve[i]].cap);
      f -= addflow;
      res += addflow * potential[t];
      for (int i = t; i != s; i = prevv[i]) {
        edge &e = graph[prevv[i]][preve[i]];
        e.cap -= addflow;
        graph[i][e.rev].cap += addflow;
      }
    }
    return res;
  }
  PrimalDual(int V) : graph(V){};
};

int N, M;
char c[55][55];
bool used[55][55];
signed main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  cin >> N >> M;
  rep(i, N) rep(j, M) cin >> c[i][j];
  PrimalDual F(N * M + 2);
  int cnt = 0;
  rep(i, N) rep(j, M) {
    if (c[i][j] == 'o') {
      cnt++;
      memset(used, 0, sizeof(used));
      F.add_edge(N * M, i * M + j, 1, 0);
      F.add_edge(i * M + j, N * M + 1, 1, mod);
      used[i][j] = true;
      rep(k, N) rep(l, M) {
        if ((k != i || l != j) && used[k][l]) {
          F.add_edge(i * M + j, k * M + l, 1, mod - (k - i + l - j));
        }
        if (c[k + 1][l] != '#')
          used[k + 1][l] |= used[k][l];
        if (c[k][l + 1] != '#')
          used[k][l + 1] |= used[k][l];
      }
    } else
      F.add_edge(i * M + j, N * M + 1, 1, 0);
  }
  cout << cnt * mod - F.minCostFlow(N * M, N * M + 1, cnt) << "\\n";
}

#include <bits/stdc++.h>
#pragma GCC optimization("Ofast")
#pragma GCC optimization("unroll-loops")
using namespace std;
#define int long long
#define rep(i, n) for (int i = 0; i < n; i++)
#define REP(i, n) for (int i = 1; i < n; i++)
#define rev(i, n) for (int i = n - 1; i >= 0; i--)
#define all(v) v.begin(), v.end()
#define P pair<int, int>
#define len(s) (int)s.size()

template <class T> inline bool chmin(T &a, T b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
template <class T> inline bool chmax(T &a, T b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}
constexpr int mod = 1e9 + 7;
constexpr long long inf = 3e18;
struct PrimalDual {
  struct edge {
    int to, cap, cost, rev;
  };
  vector<vector<edge>> graph;
  bool negative = false;
  void add_edge(int from, int to, int cap, int cost) {
    graph[from].push_back({to, cap, cost, len(graph[to])});
    graph[to].push_back({from, 0ll, -cost, len(graph[from]) - 1});
    if (cost < 0)
      negative = true;
  }
  int minCostFlow(int s, int t, int f) {
    int V = len(graph);
    vector<int> potential(V), minCost, prevv(V, -1), preve(V, -1);
    int res = 0;
    if (negative) {
      minCost.assign(V, inf);
      minCost[s] = 0;
      rep(_, V - 1) {
        rep(i, V) {
          rep(j, len(graph[i])) {
            edge &e = graph[i][j];
            if (e.cap > 0 &&
                chmin(minCost[e.to],
                      minCost[i] + e.cost + potential[i] - potential[e.to])) {
              prevv[e.to] = i;
              preve[e.to] = j;
            }
          }
        }
      }
      if (minCost[t] == inf)
        return -1;
      rep(i, V) potential[i] += minCost[i];
      for (int i = t; i != s; i = prevv[i]) {
        graph[prevv[i]][preve[i]].cap--;
        graph[i][graph[prevv[i]][preve[i]].rev].cap++;
      }
      res += potential[t];
    }
    vector<bool> used(V);
    while (f > 0) {
      minCost.assign(V, inf);
      used.assign(V, 0);
      minCost[s] = 0;
      priority_queue<P, vector<P>, greater<>> que;
      que.push({0, s});
      while (!que.empty()) {
        P p = que.top();
        que.pop();
        if (minCost[p.second] < p.first)
          continue;
        rep(i, len(graph[p.second])) {
          edge &e = graph[p.second][i];
          if (e.cap > 0 &&
              chmin(minCost[e.to],
                    p.first + e.cost + potential[p.second] - potential[e.to])) {
            prevv[e.to] = p.second;
            preve[e.to] = i;
            que.push({minCost[e.to], e.to});
          }
        }
      }

      if (minCost[t] == inf)
        return -1;
      rep(i, V) potential[i] += minCost[i];
      int addflow = f;
      for (int i = t; i != s; i = prevv[i])
        chmin(addflow, graph[prevv[i]][preve[i]].cap);
      f -= addflow;
      res += addflow * potential[t];
      for (int i = t; i != s; i = prevv[i]) {
        edge &e = graph[prevv[i]][preve[i]];
        e.cap -= addflow;
        graph[i][e.rev].cap += addflow;
      }
    }
    return res;
  }
  PrimalDual(int V) : graph(V){};
};

int N, M;
char c[55][55];
bool used[55][55];
signed main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  cin >> N >> M;
  rep(i, N) rep(j, M) cin >> c[i][j];
  PrimalDual F(N * M + 2);
  int cnt = 0;
  rep(i, N) rep(j, M) {
    if (c[i][j] == 'o') {
      cnt++;
      memset(used, 0, sizeof(used));
      F.add_edge(N * M, i * M + j, 1, 0);
      F.add_edge(i * M + j, N * M + 1, 1, mod);
      used[i][j] = true;
      rep(k, N) rep(l, M) {
        if ((k != i || l != j) && used[k][l]) {
          F.add_edge(i * M + j, k * M + l, 1, mod - (k - i + l - j));
        }
        if (c[k + 1][l] != '#')
          used[k + 1][l] |= used[k][l];
        if (c[k][l + 1] != '#')
          used[k][l + 1] |= used[k][l];
      }
    } else
      F.add_edge(i * M + j, N * M + 1, 1, 0);
  }
  cout << cnt * mod - F.minCostFlow(N * M, N * M + 1, cnt) << "\\n";
}


#include <bits/stdc++.h>

using namespace std;

using LL = long long;
const int INF = (int)1e9 + 7;
const int NS = (int)50 + 4;
int N, M;
char arr[NS][NS];
int chk[NS][NS];
vector<vector<int>> way;
vector<int> now;
vector<pair<int, int>> pos;
int augchk[NS * NS], l[NS * NS], r[NS * NS], chkN;

void dfs(int x, int y) {
  if (chk[x][y] || x > N || y > M || arr[x][y] == '#')
    return;
  chk[x][y] = 1;
  dfs(x + 1, y), dfs(x, y + 1);
}

int aug(int x) {
  for (auto &i : way[x]) {
    if (augchk[i] == chkN)
      continue;
    augchk[i] = chkN;
    if (r[i] == -1 || aug(r[i])) {
      r[i] = x;
      l[x] = i;
      return 1;
    }
  }
  return 0;
}

int main() {
  scanf("%d %d", &N, &M);
  for (int i = 1; i <= N; ++i) {
    scanf("%s", arr[i] + 1);
  }
  for (int i = 1; i <= N; ++i) {
    for (int j = 1; j <= M; ++j) {
      if (arr[i][j] == 'o') {
        now.clear();
        memset(chk, 0, sizeof(chk));
        dfs(i, j);
        for (int k = N + M; k >= 2; --k) {
          int a = 1, b = k - 1;
          while (b >= 1) {
            if (chk[a][b]) {
              now.push_back((a - 1) * M + b);
            }
            ++a, --b;
          }
        }
        way.push_back(now);
        pos.push_back(make_pair(i, j));
      }
    }
  }
  memset(r, -1, sizeof(r));
  for (int i = 0; i < (int)way.size(); ++i) {
    ++chkN;
    aug(i);
  }
  int ans = 0;
  for (int i = 0; i < (int)way.size(); ++i) {
    int x = (l[i] - 1) / M + 1, y = (l[i] - 1) % M + 1;
    ans += x + y - pos[i].first - pos[i].second;
  }
  printf("%d\\n", ans);
  return 0;
}

#include <bits/stdc++.h>

using namespace std;

using LL = long long;
const int INF = (int)1e9 + 7;
const int NS = (int)50 + 4;
int N, M;
char arr[NS][NS];
int chk[NS][NS];
vector<vector<int>> way;
vector<int> now;
vector<pair<int, int>> pos;
int augchk[NS * NS], l[NS * NS], r[NS * NS], chkN;

void dfs(int x, int y) {
  if (chk[x][y] || x < 1 || y < 1 || arr[x][y] == '#')
    return;
  chk[x][y] = 1;
  dfs(x - 1, y), dfs(x, y - 1);
}

int aug(int x) {
  for (auto &i : way[x]) {
    if (augchk[i] == chkN)
      continue;
    augchk[i] = chkN;
    if (r[i] == -1 || aug(r[i])) {
      r[i] = x;
      l[x] = i;
      return 1;
    }
  }
  return 0;
}

int main() {
  scanf("%d %d", &N, &M);
  //    N = M = 50;
  for (int i = 1; i <= N; ++i) {
    scanf("%s", arr[i] + 1);
    //        for(int j = 1; j <= M; ++j){
    //            arr[i][j] = (i <= 2 ? 'o' : '.');
    //        }
  }
  for (int I = N + M; I >= 2; --I) {
    int i = 1, j = I - i;
    while (j >= 1) {
      if (i <= N && j <= M && arr[i][j] != '#') {
        now.clear();
        memset(chk, 0, sizeof(chk));
        dfs(i, j);
        for (int k = N + M; k >= 2; --k) {
          int a = 1, b = k - 1;
          while (b >= 1) {
            if (a <= N && b <= M && chk[a][b] && arr[a][b] == 'o') {
              now.push_back((a - 1) * M + b);
            }
            ++a, --b;
          }
        }
        way.push_back(now);
        pos.push_back(make_pair(i, j));
      }
      ++i, --j;
    }
  }
  memset(r, -1, sizeof(r));
  for (int i = 0; i < (int)way.size(); ++i) {
    ++chkN;
    aug(i);
  }
  int ans = 0;
  for (int i = 0; i < (int)way.size(); ++i) {
    if (l[i]) {
      int x = (l[i] - 1) / M + 1, y = (l[i] - 1) % M + 1;
      //            printf("%d %d %d %d\\n", pos[i].first, pos[i].second, x, y);
      ans += -(x + y - pos[i].first - pos[i].second);
    }
  }
  printf("%d\\n", ans);
  return 0;
}


#include <bits/stdc++.h>

#ifndef ATCODER_MINCOSTFLOW_HPP
#define ATCODER_MINCOSTFLOW_HPP 1

namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
public:
  mcf_graph() {}
  mcf_graph(int n) : _n(n), g(n) {}

  int add_edge(int from, int to, Cap cap, Cost cost) {
    assert(0 <= from && from < _n);
    assert(0 <= to && to < _n);
    int m = int(pos.size());
    pos.push_back({from, int(g[from].size())});
    int from_id = int(g[from].size());
    int to_id = int(g[to].size());
    if (from == to)
      to_id++;
    g[from].push_back(_edge{to, to_id, cap, cost});
    g[to].push_back(_edge{from, from_id, 0, -cost});
    return m;
  }

  struct edge {
    int from, to;
    Cap cap, flow;
    Cost cost;
  };

  edge get_edge(int i) {
    int m = int(pos.size());
    assert(0 <= i && i < m);
    auto _e = g[pos[i].first][pos[i].second];
    auto _re = g[_e.to][_e.rev];
    return edge{
        pos[i].first, _e.to, _e.cap + _re.cap, _re.cap, _e.cost,
    };
  }
  std::vector<edge> edges() {
    int m = int(pos.size());
    std::vector<edge> result(m);
    for (int i = 0; i < m; i++) {
      result[i] = get_edge(i);
    }
    return result;
  }

  std::pair<Cap, Cost> flow(int s, int t) {
    return flow(s, t, std::numeric_limits<Cap>::max());
  }
  std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
    return slope(s, t, flow_limit).back();
  }
  std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
    return slope(s, t, std::numeric_limits<Cap>::max());
  }
  std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
    assert(0 <= s && s < _n);
    assert(0 <= t && t < _n);
    assert(s != t);
    // variants (C = maxcost):
    // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
    // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge
    std::vector<Cost> dual(_n, 0), dist(_n);
    std::vector<int> pv(_n), pe(_n);
    std::vector<bool> vis(_n);
    auto dual_ref = [&]() {
      std::fill(dist.begin(), dist.end(), std::numeric_limits<Cost>::max());
      std::fill(pv.begin(), pv.end(), -1);
      std::fill(pe.begin(), pe.end(), -1);
      std::fill(vis.begin(), vis.end(), false);
      struct Q {
        Cost key;
        int to;
        bool operator<(Q r) const { return key > r.key; }
      };
      std::priority_queue<Q> que;
      dist[s] = 0;
      que.push(Q{0, s});
      while (!que.empty()) {
        int v = que.top().to;
        que.pop();
        if (vis[v])
          continue;
        vis[v] = true;
        if (v == t)
          break;
        // dist[v] = shortest(s, v) + dual[s] - dual[v]
        // dist[v] >= 0 (all reduced cost are positive)
        // dist[v] <= (n-1)C
        for (int i = 0; i < int(g[v].size()); i++) {
          auto e = g[v][i];
          if (vis[e.to] || !e.cap)
            continue;
          // |-dual[e.to] + dual[v]| <= (n-1)C
          // cost <= C - -(n-1)C + 0 = nC
          Cost cost = e.cost - dual[e.to] + dual[v];
          if (dist[e.to] - dist[v] > cost) {
            dist[e.to] = dist[v] + cost;
            pv[e.to] = v;
            pe[e.to] = i;
            que.push(Q{dist[e.to], e.to});
          }
        }
      }
      if (!vis[t]) {
        return false;
      }

      for (int v = 0; v < _n; v++) {
        if (!vis[v])
          continue;
        // dual[v] = dual[v] - dist[t] + dist[v]
        //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) +
        //         (shortest(s, v) + dual[s] - dual[v]) = - shortest(s, t) +
        //         dual[t] + shortest(s, v) = shortest(s, v) - shortest(s, t) >=
        //         0 - (n-1)C
        dual[v] -= dist[t] - dist[v];
      }
      return true;
    };
    Cap flow = 0;
    Cost cost = 0, prev_cost_per_flow = -1;
    std::vector<std::pair<Cap, Cost>> result;
    result.push_back({flow, cost});
    while (flow < flow_limit) {
      if (!dual_ref())
        break;
      Cap c = flow_limit - flow;
      for (int v = t; v != s; v = pv[v]) {
        c = std::min(c, g[pv[v]][pe[v]].cap);
      }
      for (int v = t; v != s; v = pv[v]) {
        auto &e = g[pv[v]][pe[v]];
        e.cap -= c;
        g[v][e.rev].cap += c;
      }
      Cost d = -dual[s];
      flow += c;
      cost += c * d;
      if (prev_cost_per_flow == d) {
        result.pop_back();
      }
      result.push_back({flow, cost});
      prev_cost_per_flow = d;
    }
    return result;
  }

private:
  int _n;

  struct _edge {
    int to, rev;
    Cap cap;
    Cost cost;
  };

  std::vector<std::pair<int, int>> pos;
  std::vector<std::vector<_edge>> g;
};

} // namespace atcoder

#endif // ATCODER_MINCOSTFLOW_HPP

#include <boost/hana/functional/fix.hpp>

template <typename T, typename = void> struct is_iterable : std::false_type {};
template <typename T>
struct is_iterable<T, std::void_t<decltype(std::begin(std::declval<T>())),
                                  decltype(std::end(std::declval<T>()))>>
    : std::true_type {};

template <typename T, typename = void> struct is_pair : std::false_type {};
template <typename T>
struct is_pair<T, std::void_t<decltype(std::declval<T>().first),
                              decltype(std::declval<T>().second)>>
    : std::true_type {};

template <typename T> void debug(const T &v) {
  if constexpr (is_pair<T>::value) {
    std::cerr << "{";
    debug(v.first);
    std::cerr << ", ";
    debug(v.second);
    std::cerr << "}";
  } else if constexpr (is_iterable<T>::value &&
                       !std::is_same<T, std::string>::value) {
    std::cerr << "{";
    for (auto it = std::begin(v); it != std::end(v); ++it) {
      if (it != std::begin(v))
        std::cerr << ", ";
      debug(*it);
    }
    std::cerr << "}";
  } else {
    std::cerr << v;
  }
}
template <typename T, typename... Ts>
void debug(const T &value, const Ts &...args) {
  debug(value);
  std::cerr << ", ";
  debug(args...);
}
#if DEBUG
#define dbg(...)                                                               \\
  do {                                                                         \\
    cerr << #__VA_ARGS__ << ": ";                                              \\
    debug(__VA_ARGS__);                                                        \\
    cerr << " (L" << __LINE__ << ")\\n";                                        \\
  } while (0)
#else
#define dbg(...)
#endif

void read_from_cin() {}
template <typename T, typename... Ts>
void read_from_cin(T &value, Ts &...args) {
  std::cin >> value;
  read_from_cin(args...);
}
#define rd(type, ...)                                                          \\
  type __VA_ARGS__;                                                            \\
  read_from_cin(__VA_ARGS__);
#define ints(...) rd(int, __VA_ARGS__);
#define strings(...) rd(string, __VA_ARGS__);

template <typename T> void write_to_cout(const T &value) {
  if constexpr (std::is_same<T, bool>::value) {
    std::cout << (value ? "Yes" : "No");
  } else {
    std::cout << value;
  }
}
template <typename T, typename... Ts>
void write_to_cout(const T &value, const Ts &...args) {
  write_to_cout(value);
  std::cout << ' ';
  write_to_cout(args...);
}
#define wt(...)                                                                \\
  do {                                                                         \\
    write_to_cout(__VA_ARGS__);                                                \\
    cout << '\\n';                                                              \\
  } while (0)

#define all(x) (x).begin(), (x).end()

#define rep_dispatch(_1, _2, _3, name, ...) name

#define rep3(i, a, b) for (int i = (int)(a); i < (int)(b); ++i)
#define rep2(i, n) rep3(i, 0, n)
#define rep1(n) rep2(_loop_variable_, n)
#define rep(...) rep_dispatch(__VA_ARGS__, rep3, rep2, rep1)(__VA_ARGS__)

#define rrep3(i, a, b) for (int i = (int)(b)-1; i >= a; --i)
#define rrep2(i, n) rrep3(i, 0, n)
#define rrep1(n) rrep2(_loop_variable_, n)
#define rrep(...) rep_dispatch(__VA_ARGS__, rrep3, rrep2, rrep1)(__VA_ARGS__)

template <typename T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
  for (T &vi : v)
    is >> vi;
  return is;
}

template <typename T, typename U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {
  is >> p.first >> p.second;
  return is;
}

template <typename T, typename U> bool chmax(T &a, U b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}

template <typename T, typename U> bool chmin(T &a, U b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}

template <typename T, typename U> T max(T a, U b) { return a > b ? a : b; }

template <typename T, typename U> T mix(T a, U b) { return a < b ? a : b; }

template <typename T> int sz(const T &v) { return v.size(); }

template <typename T> int popcount(T i) {
  return std::bitset<std::numeric_limits<T>::digits>(i).count();
}

using i64 = std::int64_t;
using i32 = std::int32_t;

template <typename T>
using low_priority_queue =
    std::priority_queue<T, std::vector<T>, std::greater<T>>;

template <typename T> using V = std::vector<T>;
template <typename T> using VV = V<V<T>>;

void Main();

int main() {
  Main();
  return 0;
}

const auto &Fix = boost::hana::fix;

using namespace std;

#define int i64

void Main() {
  ints(n, m);
  V<string> s(n);
  cin >> s;

  V<pair<int, int>> p;
  V<VV<int>> dps;
  rep(r, n) rep(c, m) {
    if (s[r][c] != 'o')
      continue;
    // const int pi = sz(p.size());
    p.push_back({r, c});
    VV<int> dp(n, V<int>(m, -1));
    dp[r][c] = 0;
    rep(nr, r, n) rep(nc, c, n) {
      if (s[nr][nc] == '#')
        continue;
      if (nr > 0 && dp[nr - 1][nc] != -1)
        chmax(dp[nr][nc], dp[nr - 1][nc] + 1);
      if (nc > 0 && dp[nr][nc - 1] != -1)
        chmax(dp[nr][nc], dp[nr][nc - 1] + 1);
    }
    // dbg(r, c);
    // rep(i, n) dbg(dp[i]);
    dps.push_back(dp);
  }

  int src = sz(p) + n * m, dst = src + 1;
  atcoder::mcf_graph<int, int> g(dst + 1);
  const int big = n * m * 2;
  rep(i, sz(p)) {
    // if (i != 7) continue;
    // dbg(src, i);
    g.add_edge(src, i, 1, 0);
    rep(r, n) rep(c, m) if (int cap = dps[i][r][c]; cap != -1) {
      // dbg(i, r, c, cap);
      int index = sz(p) + n * r + c;
      // dbg(i, index, dst);
      g.add_edge(i, index, 1, big - cap);
    }
  }
  rep(r, n) rep(c, m) {
    int index = sz(p) + n * r + c;
    g.add_edge(index, dst, 1, 0);
  }

  auto [cap, cost] = g.flow(src, dst);
  // dbg(cap, cost);
  wt(cap * big - cost);

  V<string> res(s);
  rep(r, n) rep(c, m) if (res[r][c] == 'o') res[r][c] = '.';

  for (const auto &e : g.edges()) {
    if (e.flow == 0)
      continue;
    // dbg(e.from, e.to, e.cap, e.flow, e.cost);
    if (e.to == dst && e.flow == 1) {
      int rc = e.from - sz(p);
      int r = rc / n;
      int c = rc % n;
      dbg(r, c);
      res[r][c] = 'o';
    }
  }
  // rep(i, n) wt(res[i]);
}

#include <bits/stdc++.h>

#ifndef ATCODER_MINCOSTFLOW_HPP
#define ATCODER_MINCOSTFLOW_HPP 1

namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
public:
  mcf_graph() {}
  mcf_graph(int n) : _n(n), g(n) {}

  int add_edge(int from, int to, Cap cap, Cost cost) {
    assert(0 <= from && from < _n);
    assert(0 <= to && to < _n);
    int m = int(pos.size());
    pos.push_back({from, int(g[from].size())});
    int from_id = int(g[from].size());
    int to_id = int(g[to].size());
    if (from == to)
      to_id++;
    g[from].push_back(_edge{to, to_id, cap, cost});
    g[to].push_back(_edge{from, from_id, 0, -cost});
    return m;
  }

  struct edge {
    int from, to;
    Cap cap, flow;
    Cost cost;
  };

  edge get_edge(int i) {
    int m = int(pos.size());
    assert(0 <= i && i < m);
    auto _e = g[pos[i].first][pos[i].second];
    auto _re = g[_e.to][_e.rev];
    return edge{
        pos[i].first, _e.to, _e.cap + _re.cap, _re.cap, _e.cost,
    };
  }
  std::vector<edge> edges() {
    int m = int(pos.size());
    std::vector<edge> result(m);
    for (int i = 0; i < m; i++) {
      result[i] = get_edge(i);
    }
    return result;
  }

  std::pair<Cap, Cost> flow(int s, int t) {
    return flow(s, t, std::numeric_limits<Cap>::max());
  }
  std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
    return slope(s, t, flow_limit).back();
  }
  std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
    return slope(s, t, std::numeric_limits<Cap>::max());
  }
  std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
    assert(0 <= s && s < _n);
    assert(0 <= t && t < _n);
    assert(s != t);
    // variants (C = maxcost):
    // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
    // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge
    std::vector<Cost> dual(_n, 0), dist(_n);
    std::vector<int> pv(_n), pe(_n);
    std::vector<bool> vis(_n);
    auto dual_ref = [&]() {
      std::fill(dist.begin(), dist.end(), std::numeric_limits<Cost>::max());
      std::fill(pv.begin(), pv.end(), -1);
      std::fill(pe.begin(), pe.end(), -1);
      std::fill(vis.begin(), vis.end(), false);
      struct Q {
        Cost key;
        int to;
        bool operator<(Q r) const { return key > r.key; }
      };
      std::priority_queue<Q> que;
      dist[s] = 0;
      que.push(Q{0, s});
      while (!que.empty()) {
        int v = que.top().to;
        que.pop();
        if (vis[v])
          continue;
        vis[v] = true;
        if (v == t)
          break;
        // dist[v] = shortest(s, v) + dual[s] - dual[v]
        // dist[v] >= 0 (all reduced cost are positive)
        // dist[v] <= (n-1)C
        for (int i = 0; i < int(g[v].size()); i++) {
          auto e = g[v][i];
          if (vis[e.to] || !e.cap)
            continue;
          // |-dual[e.to] + dual[v]| <= (n-1)C
          // cost <= C - -(n-1)C + 0 = nC
          Cost cost = e.cost - dual[e.to] + dual[v];
          if (dist[e.to] - dist[v] > cost) {
            dist[e.to] = dist[v] + cost;
            pv[e.to] = v;
            pe[e.to] = i;
            que.push(Q{dist[e.to], e.to});
          }
        }
      }
      if (!vis[t]) {
        return false;
      }

      for (int v = 0; v < _n; v++) {
        if (!vis[v])
          continue;
        // dual[v] = dual[v] - dist[t] + dist[v]
        //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) +
        //         (shortest(s, v) + dual[s] - dual[v]) = - shortest(s, t) +
        //         dual[t] + shortest(s, v) = shortest(s, v) - shortest(s, t) >=
        //         0 - (n-1)C
        dual[v] -= dist[t] - dist[v];
      }
      return true;
    };
    Cap flow = 0;
    Cost cost = 0, prev_cost_per_flow = -1;
    std::vector<std::pair<Cap, Cost>> result;
    result.push_back({flow, cost});
    while (flow < flow_limit) {
      if (!dual_ref())
        break;
      Cap c = flow_limit - flow;
      for (int v = t; v != s; v = pv[v]) {
        c = std::min(c, g[pv[v]][pe[v]].cap);
      }
      for (int v = t; v != s; v = pv[v]) {
        auto &e = g[pv[v]][pe[v]];
        e.cap -= c;
        g[v][e.rev].cap += c;
      }
      Cost d = -dual[s];
      flow += c;
      cost += c * d;
      if (prev_cost_per_flow == d) {
        result.pop_back();
      }
      result.push_back({flow, cost});
      prev_cost_per_flow = d;
    }
    return result;
  }

private:
  int _n;

  struct _edge {
    int to, rev;
    Cap cap;
    Cost cost;
  };

  std::vector<std::pair<int, int>> pos;
  std::vector<std::vector<_edge>> g;
};

} // namespace atcoder

#endif // ATCODER_MINCOSTFLOW_HPP

#include <boost/hana/functional/fix.hpp>

template <typename T, typename = void> struct is_iterable : std::false_type {};
template <typename T>
struct is_iterable<T, std::void_t<decltype(std::begin(std::declval<T>())),
                                  decltype(std::end(std::declval<T>()))>>
    : std::true_type {};

template <typename T, typename = void> struct is_pair : std::false_type {};
template <typename T>
struct is_pair<T, std::void_t<decltype(std::declval<T>().first),
                              decltype(std::declval<T>().second)>>
    : std::true_type {};

template <typename T> void debug(const T &v) {
  if constexpr (is_pair<T>::value) {
    std::cerr << "{";
    debug(v.first);
    std::cerr << ", ";
    debug(v.second);
    std::cerr << "}";
  } else if constexpr (is_iterable<T>::value &&
                       !std::is_same<T, std::string>::value) {
    std::cerr << "{";
    for (auto it = std::begin(v); it != std::end(v); ++it) {
      if (it != std::begin(v))
        std::cerr << ", ";
      debug(*it);
    }
    std::cerr << "}";
  } else {
    std::cerr << v;
  }
}
template <typename T, typename... Ts>
void debug(const T &value, const Ts &...args) {
  debug(value);
  std::cerr << ", ";
  debug(args...);
}
#if DEBUG
#define dbg(...)                                                               \\
  do {                                                                         \\
    cerr << #__VA_ARGS__ << ": ";                                              \\
    debug(__VA_ARGS__);                                                        \\
    cerr << " (L" << __LINE__ << ")\\n";                                        \\
  } while (0)
#else
#define dbg(...)
#endif

void read_from_cin() {}
template <typename T, typename... Ts>
void read_from_cin(T &value, Ts &...args) {
  std::cin >> value;
  read_from_cin(args...);
}
#define rd(type, ...)                                                          \\
  type __VA_ARGS__;                                                            \\
  read_from_cin(__VA_ARGS__);
#define ints(...) rd(int, __VA_ARGS__);
#define strings(...) rd(string, __VA_ARGS__);

template <typename T> void write_to_cout(const T &value) {
  if constexpr (std::is_same<T, bool>::value) {
    std::cout << (value ? "Yes" : "No");
  } else {
    std::cout << value;
  }
}
template <typename T, typename... Ts>
void write_to_cout(const T &value, const Ts &...args) {
  write_to_cout(value);
  std::cout << ' ';
  write_to_cout(args...);
}
#define wt(...)                                                                \\
  do {                                                                         \\
    write_to_cout(__VA_ARGS__);                                                \\
    cout << '\\n';                                                              \\
  } while (0)

#define all(x) (x).begin(), (x).end()

#define rep_dispatch(_1, _2, _3, name, ...) name

#define rep3(i, a, b) for (int i = (int)(a); i < (int)(b); ++i)
#define rep2(i, n) rep3(i, 0, n)
#define rep1(n) rep2(_loop_variable_, n)
#define rep(...) rep_dispatch(__VA_ARGS__, rep3, rep2, rep1)(__VA_ARGS__)

#define rrep3(i, a, b) for (int i = (int)(b)-1; i >= a; --i)
#define rrep2(i, n) rrep3(i, 0, n)
#define rrep1(n) rrep2(_loop_variable_, n)
#define rrep(...) rep_dispatch(__VA_ARGS__, rrep3, rrep2, rrep1)(__VA_ARGS__)

template <typename T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
  for (T &vi : v)
    is >> vi;
  return is;
}

template <typename T, typename U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {
  is >> p.first >> p.second;
  return is;
}

template <typename T, typename U> bool chmax(T &a, U b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}

template <typename T, typename U> bool chmin(T &a, U b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}

template <typename T, typename U> T max(T a, U b) { return a > b ? a : b; }

template <typename T, typename U> T mix(T a, U b) { return a < b ? a : b; }

template <typename T> int sz(const T &v) { return v.size(); }

template <typename T> int popcount(T i) {
  return std::bitset<std::numeric_limits<T>::digits>(i).count();
}

using i64 = std::int64_t;
using i32 = std::int32_t;

template <typename T>
using low_priority_queue =
    std::priority_queue<T, std::vector<T>, std::greater<T>>;

template <typename T> using V = std::vector<T>;
template <typename T> using VV = V<V<T>>;

void Main();

int main() {
  Main();
  return 0;
}

const auto &Fix = boost::hana::fix;

using namespace std;

#define int i64

void Main() {
  ints(n, m);
  V<string> s(n);
  cin >> s;

  V<pair<int, int>> p;
  V<VV<int>> dps;
  rep(r, n) rep(c, m) {
    if (s[r][c] != 'o')
      continue;
    // const int pi = sz(p.size());
    p.push_back({r, c});
    VV<int> dp(n, V<int>(m, -1));
    dp[r][c] = 0;
    rep(nr, r, n) rep(nc, c, m) {
      if (s[nr][nc] == '#')
        continue;
      if (nr > 0 && dp[nr - 1][nc] != -1)
        chmax(dp[nr][nc], dp[nr - 1][nc] + 1);
      if (nc > 0 && dp[nr][nc - 1] != -1)
        chmax(dp[nr][nc], dp[nr][nc - 1] + 1);
    }
    dps.push_back(dp);
  }

  int src = sz(p) + n * m, dst = src + 1;
  atcoder::mcf_graph<int, int> g(dst + 1);
  const int big = n * m * 2;
  rep(i, sz(p)) {
    g.add_edge(src, i, 1, 0);
    rep(r, n) rep(c, m) if (int cap = dps[i][r][c]; cap != -1) {
      int index = sz(p) + m * r + c;
      g.add_edge(i, index, 1, big - cap);
    }
  }
  rep(r, n) rep(c, m) {
    int index = sz(p) + m * r + c;
    g.add_edge(index, dst, 1, 0);
  }

  auto [cap, cost] = g.flow(src, dst);
  wt(cap * big - cost);

  V<string> res(s);
  rep(r, n) rep(c, m) if (res[r][c] == 'o') res[r][c] = '.';

  for (const auto &e : g.edges()) {
    if (e.flow == 0)
      continue;
    if (e.to == dst && e.flow == 1) {
      int rc = e.from - sz(p);
      int r = rc / m;
      int c = rc % m;
      dbg(r, c);
      res[r][c] = 'o';
    }
  }
  rep(i, n) dbg(res[i]);
}


#include <bits/stdc++.h>
using namespace std;

const int N = 50 * 50 + 200;

struct MCMF { // 1 indexed
  using T = int;
  static const T inf = 1e9 + 7;
  int n, m, s, t;
  bool vis[N];
  int par[N], pos[N];
  T pot[N], dis[N], left[N];
  priority_queue<pair<T, int>> q;
  struct edge {
    int to, rev;
    T cap, cost, flow;
    int id;
  };
  vector<edge> ed[N];
  MCMF() {}
  MCMF(int _n) {
    n = _n + 2;
    for (int i = 1; i <= n; i++)
      ed[i].clear();
  }
  void add_edge(int u, int v, T cap, T cost, int id = -1) {
    edge a = {v, (int)ed[v].size(), cap, cost, 0, id};
    edge b = {u, (int)ed[u].size(), 0, -cost, 0, -1};
    ed[u].push_back(a);
    ed[v].push_back(b);
  }
  T BellmanFord(int u) {
    if (vis[u])
      return pot[u];
    if (u == s) {
      pot[s] = 0;
      return 0;
    }
    vis[u] = true;
    pot[u] = inf;
    for (edge e : ed[u]) {
      edge r = ed[e.to][e.rev];
      if (r.flow < r.cap)
        pot[u] = min(pot[u], BellmanFord(e.to) + r.cost);
    }
    return pot[u];
  }
  bool Dijkstra() {
    memset(vis, 0, sizeof vis);
    for (int i = 1; i <= n; i++)
      dis[i] = left[i] = inf;
    dis[s] = 0;
    q.push({0, s});
    int u, v;
    while (!q.empty()) {
      u = q.top().second;
      q.pop();
      if (vis[u])
        continue;
      vis[u] = true;
      int ptr = 0;
      for (edge e : ed[u]) {
        v = e.to;
        T cost = e.cost + pot[u] - pot[v];
        if (e.flow < e.cap && dis[u] + cost < dis[v]) {
          dis[v] = dis[u] + cost;
          par[v] = u;
          pos[v] = ptr;
          left[v] = min(left[u], e.cap - e.flow);
          q.push(make_pair(-dis[v], v));
        }
        ++ptr;
      }
    }
    for (int i = 1; i <= n; i++)
      dis[i] += (pot[i] - pot[s]);
    return vis[t];
  }
  pair<T, T> solve(int _s, int _t, T goal = inf) {
    s = _s;
    t = _t;
    memset(pot, 0, sizeof pot);
    memset(vis, 0, sizeof vis);
    BellmanFord(t);
    int u, v;
    T ans = 0, cost = 0, f;
    while (ans < goal && Dijkstra()) {
      u = t;
      f = left[t];
      while (u != s) {
        v = par[u];
        ed[v][pos[u]].flow += left[t];
        ed[u][ed[v][pos[u]].rev].flow -= left[t];
        u = v;
      }
      T need = goal - ans;
      f = min(f, need);
      ans += f;
      cost += f * dis[t];
      memcpy(pot, dis, sizeof dis);
    }
    return make_pair(ans, cost);
  }
};
string a[55];
int32_t main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  int n, m;
  cin >> n >> m;
  for (int i = 0; i < n; i++)
    cin >> a[i];
  MCMF F(N - 10);
  int s = N - 12, t = N - 13;
  int cnt = 1;
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
      function<void(int, int)> dfs = [&](int x, int y) {
        if (a[x][y] == '#')
          return;
        F.add_edge(cnt, x * m + y + 1 + 100, 1, -(x - i + y - j));
        if (x + 1 < n)
          dfs(x + 1, y);
        if (y + 1 < m)
          dfs(x, y + 1);
      };
      if (a[i][j] == '#')
        continue;
      if (a[i][j] == 'o') {
        F.add_edge(s, cnt, 1, 0);
        dfs(i, j);
        ++cnt;
      }
      F.add_edge(i * m + j + 1 + 100, t, 1, 0);
    }
  }
  cout << -F.solve(s, t).second << '\\n';
  return 0;
}

#include <bits/stdc++.h>
using namespace std;

const int N = 50 * 50 + 200;

struct MCMF { // 1 indexed
  using T = int;
  static const T inf = 1e9 + 7;
  int n, m, s, t;
  bool vis[N];
  int par[N], pos[N];
  T pot[N], dis[N], left[N];
  priority_queue<pair<T, int>> q;
  struct edge {
    int to, rev;
    T cap, cost, flow;
    int id;
  };
  vector<edge> ed[N];
  MCMF() {}
  MCMF(int _n) {
    n = _n + 2;
    for (int i = 1; i <= n; i++)
      ed[i].clear();
  }
  void add_edge(int u, int v, T cap, T cost, int id = -1) {
    edge a = {v, (int)ed[v].size(), cap, cost, 0, id};
    edge b = {u, (int)ed[u].size(), 0, -cost, 0, -1};
    ed[u].push_back(a);
    ed[v].push_back(b);
  }
  T BellmanFord(int u) {
    if (vis[u])
      return pot[u];
    if (u == s) {
      pot[s] = 0;
      return 0;
    }
    vis[u] = true;
    pot[u] = inf;
    for (edge e : ed[u]) {
      edge r = ed[e.to][e.rev];
      if (r.flow < r.cap)
        pot[u] = min(pot[u], BellmanFord(e.to) + r.cost);
    }
    return pot[u];
  }
  bool Dijkstra() {
    memset(vis, 0, sizeof vis);
    for (int i = 1; i <= n; i++)
      dis[i] = left[i] = inf;
    dis[s] = 0;
    q.push({0, s});
    int u, v;
    while (!q.empty()) {
      u = q.top().second;
      q.pop();
      if (vis[u])
        continue;
      vis[u] = true;
      int ptr = 0;
      for (edge e : ed[u]) {
        v = e.to;
        T cost = e.cost + pot[u] - pot[v];
        if (e.flow < e.cap && dis[u] + cost < dis[v]) {
          dis[v] = dis[u] + cost;
          par[v] = u;
          pos[v] = ptr;
          left[v] = min(left[u], e.cap - e.flow);
          q.push(make_pair(-dis[v], v));
        }
        ++ptr;
      }
    }
    for (int i = 1; i <= n; i++)
      dis[i] += (pot[i] - pot[s]);
    return vis[t];
  }
  pair<T, T> solve(int _s, int _t, T goal = inf) {
    s = _s;
    t = _t;
    memset(pot, 0, sizeof pot);
    memset(vis, 0, sizeof vis);
    BellmanFord(t);
    int u, v;
    T ans = 0, cost = 0, f;
    while (ans < goal && Dijkstra()) {
      u = t;
      f = left[t];
      while (u != s) {
        v = par[u];
        ed[v][pos[u]].flow += left[t];
        ed[u][ed[v][pos[u]].rev].flow -= left[t];
        u = v;
      }
      T need = goal - ans;
      f = min(f, need);
      ans += f;
      cost += f * dis[t];
      memcpy(pot, dis, sizeof dis);
    }
    return make_pair(ans, cost);
  }
};
string a[55];
bool vis[N][N];
int32_t main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  int n, m;
  cin >> n >> m;
  for (int i = 0; i < n; i++)
    cin >> a[i];
  MCMF F(N - 10);
  int s = N - 12, t = N - 13;
  int cnt = 1;
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
      function<void(int, int)> dfs = [&](int x, int y) {
        if (a[x][y] == '#')
          return;
        if (vis[x][y])
          return;
        vis[x][y] = 1;
        F.add_edge(cnt, x * m + y + 1 + 100, 1, -(x - i + y - j));
        if (x + 1 < n)
          dfs(x + 1, y);
        if (y + 1 < m)
          dfs(x, y + 1);
      };
      if (a[i][j] == '#')
        continue;
      if (a[i][j] == 'o') {
        F.add_edge(s, cnt, 1, 0);
        memset(vis, 0, sizeof vis);
        dfs(i, j);
        ++cnt;
      }
      F.add_edge(i * m + j + 1 + 100, t, 1, 0);
    }
  }
  cout << -F.solve(s, t).second << '\\n';
  return 0;
}


#include <stdio.h>
#include <stdlib.h>

#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <stdint.h>
#include <string.h>

#define _USE_MATH_DEFINES
#include <math.h>

#include <deque>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include <algorithm>
#include <bitset>
#include <chrono>
#include <functional>
#include <random>

#define sqr(x) (x) * (x)

typedef unsigned int u32;
typedef int i32;
typedef unsigned long long int u64;
typedef int i64;
typedef uint16_t u16;
typedef int16_t i16;
typedef uint8_t u8;
typedef int8_t i8;

using namespace std;
using namespace std::chrono;

const i64 mod = 1'000'000'000ll + 7;
// const i64 smod = 998'244'353ll;

const i64 inf = mod;

const long double eps = 1e-8;

i64 n, m;

i64 nx, mx;
vector<string> ax;

vector<pair<i64, i64>> o;

vector<vector<i16>> a;

int main(int argc, char *argv[]) {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  cout.precision(15);
  cout.setf(ios::fixed);
  cerr.precision(15);
  cerr.setf(ios::fixed);

  cin >> nx >> mx;
  ax = vector<string>(nx);
  for (i64 i = 0; i < nx; i++) {
    cin >> ax[i];
  }

  for (i64 i = 0; i < nx; i++) {
    for (i64 j = 0; j < mx; j++) {
      if (ax[i][j] == 'o') {
        o.push_back({i, j});
      }
    }
  }

  n = o.size();
  m = nx * mx;

  a = vector<vector<i16>>(n + 1, vector<i16>(m + 1, 10'500));

  for (i64 i = 0; i < n; i++) {
    vector<pair<i64, i64>> q;

    q.push_back(o[i]);

    i64 len = q.size();
    i64 ans = 0;

    for (i64 k = 0; k < q.size(); k++) {
      if (len == k) {
        ans += 1;
        len = q.size();
      }

      i64 x = q[k].first;
      i64 y = q[k].second;

      a[1 + i][1 + x * mx + y] = -ans;

      if (x + 1 < nx && ax[x + 1][y] != '#') {
        q.push_back({x + 1, y});
      }

      if (y + 1 < mx && ax[x][y + 1] != '#') {
        q.push_back({x, y + 1});
      }
    }
  }

  vector<i64> u(n + 1), v(m + 1), p(m + 1), way(m + 1);
  for (i64 i = 1; i <= n; ++i) {
    p[0] = i;
    i64 j0 = 0;
    vector<i64> minv(m + 1, inf);
    vector<char> used(m + 1, false);
    do {
      used[j0] = true;
      i64 i0 = p[j0], delta = inf, j1;
      for (i64 j = 1; j <= m; ++j)
        if (!used[j]) {
          i64 cur = (a[i0][j] == 10'500 ? inf : (i64)a[i0][j]) - u[i0] - v[j];
          if (cur < minv[j])
            minv[j] = cur, way[j] = j0;
          if (minv[j] < delta)
            delta = minv[j], j1 = j;
        }
      for (i64 j = 0; j <= m; ++j)
        if (used[j])
          u[p[j]] += delta, v[j] -= delta;
        else
          minv[j] -= delta;
      j0 = j1;
    } while (p[j0] != 0);
    do {
      i64 j1 = way[j0];
      p[j0] = p[j1];
      j0 = j1;
    } while (j0);
  }

  cout << v[0] << endl;

  return 0;
}

#include <stdio.h>
#include <stdlib.h>

#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <stdint.h>
#include <string.h>

#define _USE_MATH_DEFINES
#include <math.h>

#include <deque>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include <algorithm>
#include <bitset>
#include <chrono>
#include <functional>
#include <random>

#define sqr(x) (x) * (x)

typedef unsigned int u32;
typedef int i32;
typedef unsigned long long int u64;
typedef int i64;
typedef uint16_t u16;
typedef int16_t i16;
typedef uint8_t u8;
typedef int8_t i8;

using namespace std;
using namespace std::chrono;

const i64 mod = 1'000'000'000ll + 7;
// const i64 smod = 998'244'353ll;

const i64 inf = mod;

const long double eps = 1e-8;

i64 n, m;

i64 nx, mx;
vector<string> ax;

vector<pair<i64, i64>> o;

vector<vector<i16>> a;

int main(int argc, char *argv[]) {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  cout.precision(15);
  cout.setf(ios::fixed);
  cerr.precision(15);
  cerr.setf(ios::fixed);

  cin >> nx >> mx;
  ax = vector<string>(nx);
  for (i64 i = 0; i < nx; i++) {
    cin >> ax[i];
  }

  for (i64 i = 0; i < nx; i++) {
    for (i64 j = 0; j < mx; j++) {
      if (ax[i][j] == 'o') {
        o.push_back({i, j});
      }
    }
  }

  n = o.size();
  m = nx * mx;

  a = vector<vector<i16>>(n + 1, vector<i16>(m + 1, 10'500));

  for (i64 i = 0; i < n; i++) {
    vector<pair<i64, i64>> q;
    set<pair<i64, i64>> used;

    q.push_back(o[i]);
    used.insert(o[i]);

    i64 len = q.size();
    i64 ans = 0;

    for (i64 k = 0; k < q.size(); k++) {
      if (len == k) {
        ans += 1;
        len = q.size();
      }

      i64 x = q[k].first;
      i64 y = q[k].second;

      a[1 + i][1 + x * mx + y] = -ans;

      if (x + 1 < nx && ax[x + 1][y] != '#' && !used.count({x, y + 1})) {
        q.push_back({x + 1, y});
        used.insert({x + 1, y});
      }

      if (y + 1 < mx && ax[x][y + 1] != '#' && !used.count({x, y + 1})) {
        q.push_back({x, y + 1});
        used.insert({x, y + 1});
      }
    }
  }

  vector<i64> u(n + 1), v(m + 1), p(m + 1), way(m + 1);
  for (i64 i = 1; i <= n; ++i) {
    p[0] = i;
    i64 j0 = 0;
    vector<i64> minv(m + 1, inf);
    vector<char> used(m + 1, false);
    do {
      used[j0] = true;
      i64 i0 = p[j0], delta = inf, j1;
      for (i64 j = 1; j <= m; ++j)
        if (!used[j]) {
          i64 cur = (a[i0][j] == 10'500 ? inf : (i64)a[i0][j]) - u[i0] - v[j];
          if (cur < minv[j])
            minv[j] = cur, way[j] = j0;
          if (minv[j] < delta)
            delta = minv[j], j1 = j;
        }
      for (i64 j = 0; j <= m; ++j)
        if (used[j])
          u[p[j]] += delta, v[j] -= delta;
        else
          minv[j] -= delta;
      j0 = j1;
    } while (p[j0] != 0);
    do {
      i64 j1 = way[j0];
      p[j0] = p[j1];
      j0 = j1;
    } while (j0);
  }

  cout << v[0] << endl;

  return 0;
}


#include <queue>
#include <stdio.h>
#include <vector>
#define MAX 123456789
struct MCMF {
  struct Edge {
    int first;
    int second;
    int capacity;
    int cost;
  };
  std::vector<int> V[5010][5010];
  std::vector<Edge> E;

  void setEdge(int first, int second, int capacity, int cost) {
    int n = E.size();
    E.push_back({first, second, capacity, cost});
    E.push_back({second, first, 0, -cost});
    V[first][second].push_back(n);
    V[second][first].push_back(n + 1);
  }

  int vSize;
  int source, sink;

  int dist[5010], check[5010], prev[5010];
  std::vector<int> SPFA() {
    for (int i = 0; i <= vSize; i++)
      check[i] = 0;
    for (int i = 0; i <= vSize; i++)
      dist[i] = MAX;
    std::queue<int> Q;
    Q.push(source);
    dist[source] = 0;
    while (!Q.empty()) {
      int v = Q.front();
      Q.pop();

      check[v] = 0;
      for (int i = 0; i <= vSize; i++) {
        for (int k = 0; k < V[v][i].size(); k++) {
          int e = V[v][i][k];
          if (E[e].capacity > 0) {
            if (dist[i] > dist[v] + E[e].cost) {
              dist[i] = dist[v] + E[e].cost;
              prev[i] = e;
              if (check[i] == 0) {
                check[i] = 1;
                Q.push(i);
              }
            }
          }
        }
      }
    }

    std::vector<int> path;
    if (dist[sink] == MAX)
      return path;
    else {
      int v = sink;
      while (v != source) {
        path.push_back(prev[v]);
        v = E[prev[v]].first;
      }
      return path;
    }
  }

  std::pair<int, int> getMCMF() // <Flow,Cost>
  {
    int flow = 0, cost = 0;
    while (1) {
      std::vector<int> path = SPFA();
      if (path.size() == 0)
        return std::make_pair(flow, cost);
      else {
        int f = MAX, c = 0;
        for (int i = 0; i < path.size(); i++) {
          f = f < E[path[i]].capacity ? f : E[path[i]].capacity;
          c += E[path[i]].cost;
        }
        flow += f;
        cost += f * c;
        for (int i = 0; i < path.size(); i++) {
          E[path[i]].capacity -= f;
          E[path[i] ^ 1].capacity += f;
        }
      }
    }
  }
} G;

char x[110][110];
std::queue<std::pair<int, int>> Q;
int inQueue[110][110];

int main() {
  int a, b;
  scanf("%d%d", &a, &b);
  for (int i = 1; i <= a; i++)
    scanf("%s", x[i] + 1);
  for (int i = 1; i <= a; i++) {
    for (int j = 1; j <= b; j++) {
      if (x[i][j] == 'o') {
        Q.push(std::make_pair(i, j));
        inQueue[i][j] = 1;
        while (!Q.empty()) {
          int s = Q.front().first;
          int t = Q.front().second;
          Q.pop();

          inQueue[s][t] = 0;
          int v1 = b * (i - 1) + j;
          int v2 = a * b + b * (s - 1) + t;
          G.setEdge(v1, v2, 1, -(s - i + t - j));
          if ((x[s + 1][t] == 'o' || x[s + 1][t] == '.') &&
              inQueue[s + 1][t] == 0) {
            inQueue[s + 1][t] = 1;
            Q.push(std::make_pair(s + 1, t));
          }
          if ((x[s][t + 1] == 'o' || x[s][t + 1] == '.') &&
              inQueue[s][t + 1] == 0) {
            inQueue[s][t + 1] = 1;
            Q.push(std::make_pair(s, t + 1));
          }
        }
      }
    }
  }

  G.source = 2 * a * b + 1;
  G.sink = 2 * a * b + 2;
  G.vSize = 2 * a * b + 2;
  for (int i = 1; i <= a; i++) {
    for (int j = 1; j <= b; j++) {
      if (x[i][j] == 'o') {
        int v1 = b * (i - 1) + j;
        G.setEdge(2 * a * b + 1, v1, 1, 0);
      }
    }
  }
  for (int i = 1; i <= a; i++) {
    for (int j = 1; j <= b; j++) {
      if (x[i][j] != '#') {
        int v1 = a * b + b * (i - 1) + j;
        G.setEdge(v1, 2 * a * b + 2, 1, 0);
      }
    }
  }

  std::pair<int, int> P = G.getMCMF();
  printf("%d", -P.second);
}

#include <queue>
#include <stdio.h>
#include <vector>
#define MAX 123456789
struct MCMF {
  struct Edge {
    int first;
    int second;
    int capacity;
    int cost;
  };
  std::vector<int> V[5010][5010];
  std::vector<Edge> E;
  std::vector<int> V2[5010];

  void setEdge(int first, int second, int capacity, int cost) {
    int n = E.size();
    E.push_back({first, second, capacity, cost});
    E.push_back({second, first, 0, -cost});
    V[first][second].push_back(n);
    V[second][first].push_back(n + 1);
  }

  int vSize;
  int source, sink;

  int dist[5010], check[5010], prev[5010];
  void init() {
    for (int i = 0; i <= vSize; i++) {
      for (int j = 0; j <= vSize; j++) {
        if (V[i][j].size() > 0)
          V2[i].push_back(j);
      }
    }
  }
  std::vector<int> SPFA() {
    for (int i = 0; i <= vSize; i++)
      check[i] = 0;
    for (int i = 0; i <= vSize; i++)
      dist[i] = MAX;
    std::queue<int> Q;
    Q.push(source);
    dist[source] = 0;
    while (!Q.empty()) {
      int v = Q.front();
      Q.pop();

      check[v] = 0;
      for (int j = 0; j < V2[v].size(); j++) {
        int i = V2[v][j];
        for (int k = 0; k < V[v][i].size(); k++) {
          int e = V[v][i][k];
          if (E[e].capacity > 0) {
            if (dist[i] > dist[v] + E[e].cost) {
              dist[i] = dist[v] + E[e].cost;
              prev[i] = e;
              if (check[i] == 0) {
                check[i] = 1;
                Q.push(i);
              }
            }
          }
        }
      }
    }

    std::vector<int> path;
    if (dist[sink] == MAX)
      return path;
    else {
      int v = sink;
      while (v != source) {
        path.push_back(prev[v]);
        v = E[prev[v]].first;
      }
      return path;
    }
  }

  std::pair<int, int> getMCMF() // <Flow,Cost>
  {
    int flow = 0, cost = 0;
    while (1) {
      std::vector<int> path = SPFA();
      if (path.size() == 0)
        return std::make_pair(flow, cost);
      else {
        int f = MAX, c = 0;
        for (int i = 0; i < path.size(); i++) {
          f = f < E[path[i]].capacity ? f : E[path[i]].capacity;
          c += E[path[i]].cost;
        }
        flow += f;
        cost += f * c;
        for (int i = 0; i < path.size(); i++) {
          E[path[i]].capacity -= f;
          E[path[i] ^ 1].capacity += f;
        }
      }
    }
  }
} G;

char x[110][110];
std::queue<std::pair<int, int>> Q;
int inQueue[110][110];

int main() {
  int a, b;
  scanf("%d%d", &a, &b);
  for (int i = 1; i <= a; i++)
    scanf("%s", x[i] + 1);
  for (int i = 1; i <= a; i++) {
    for (int j = 1; j <= b; j++) {
      if (x[i][j] == 'o') {
        Q.push(std::make_pair(i, j));
        inQueue[i][j] = 1;
        while (!Q.empty()) {
          int s = Q.front().first;
          int t = Q.front().second;
          Q.pop();

          inQueue[s][t] = 0;
          int v1 = b * (i - 1) + j;
          int v2 = a * b + b * (s - 1) + t;
          G.setEdge(v1, v2, 1, -(s - i + t - j));
          if ((x[s + 1][t] == 'o' || x[s + 1][t] == '.') &&
              inQueue[s + 1][t] == 0) {
            inQueue[s + 1][t] = 1;
            Q.push(std::make_pair(s + 1, t));
          }
          if ((x[s][t + 1] == 'o' || x[s][t + 1] == '.') &&
              inQueue[s][t + 1] == 0) {
            inQueue[s][t + 1] = 1;
            Q.push(std::make_pair(s, t + 1));
          }
        }
      }
    }
  }

  G.source = 2 * a * b + 1;
  G.sink = 2 * a * b + 2;
  G.vSize = 2 * a * b + 2;
  for (int i = 1; i <= a; i++) {
    for (int j = 1; j <= b; j++) {
      if (x[i][j] == 'o') {
        int v1 = b * (i - 1) + j;
        G.setEdge(2 * a * b + 1, v1, 1, 0);
      }
    }
  }
  for (int i = 1; i <= a; i++) {
    for (int j = 1; j <= b; j++) {
      if (x[i][j] != '#') {
        int v1 = a * b + b * (i - 1) + j;
        G.setEdge(v1, 2 * a * b + 2, 1, 0);
      }
    }
  }

  G.init();
  std::pair<int, int> P = G.getMCMF();
  printf("%d", -P.second);
}


#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < n; i++)
#define rep2(i, x, n) for (int i = x; i <= n; i++)
#define rep3(i, x, n) for (int i = x; i >= n; i--)
#define elif else if
#define sp(x) fixed << setprecision(x)
#define pb push_back
#define eb emplace_back
#define all(x) x.begin(), x.end()
#define sz(x) (int)x.size()
using ll = long long;
using pii = pair<int, int>;
using pil = pair<int, ll>;
using pli = pair<ll, int>;
using pll = pair<ll, ll>;
const int MOD = 1000000007;
// const int MOD = 998244353;
const int inf = (1 << 30) - 1;
const ll INF = (1LL << 60) - 1;
const double pi = acos(-1.0);
const double EPS = 1e-10;
template <typename T> bool chmax(T &x, const T &y) {
  return (x < y) ? (x = y, true) : false;
};
template <typename T> bool chmin(T &x, const T &y) {
  return (x > y) ? (x = y, true) : false;
};

template <typename T> struct Min_Cost_Flow {
  struct edge {
    int to;
    T cap, cost;
    int rev;
    edge(int to, T cap, T cost, int rev)
        : to(to), cap(cap), cost(cost), rev(rev) {}
  };

  vector<vector<edge>> es;
  vector<T> d, h;
  vector<int> pre_v, pre_e;
  const T INF_T;
  const int n;

  Min_Cost_Flow(int n) : INF_T(numeric_limits<T>::max()), n(n) {
    es.resize(n), d.resize(n), h.resize(n), pre_v.resize(n), pre_e.resize(n);
  }

  void add_edge(int from, int to, T cap, T cost) {
    es[from].eb(to, cap, cost, sz(es[to]));
    es[to].eb(from, 0, -cost, sz(es[from]) - 1);
  }

  void dijkstra(int s) {
    fill(all(d), INF_T);
    using P = pair<T, int>;
    priority_queue<P, vector<P>, greater<P>> que;
    que.emplace(d[s] = 0, s);
    while (!que.empty()) {
      T p;
      int i;
      tie(p, i) = que.top();
      que.pop();
      if (p > d[i])
        continue;
      rep(j, sz(es[i])) {
        edge &e = es[i][j];
        if (e.cap > 0 && chmin(d[e.to], d[i] + e.cost + h[i] - h[e.to])) {
          pre_v[e.to] = i, pre_e[e.to] = j;
          que.emplace(d[e.to], e.to);
        }
      }
    }
  }

  T min_cost_flow(int s, int t, T flow) {
    T ret = 0;
    fill(all(h), 0);
    while (flow > 0) {
      dijkstra(s);
      if (d[t] == INF_T)
        return -1;
      rep(i, sz(es)) {
        if (h[i] == INF_T || d[i] == INF_T)
          h[i] = INF_T;
        else
          h[i] += d[i];
      }
      T f = flow;
      for (int now = t; now != s; now = pre_v[now]) {
        chmin(f, es[pre_v[now]][pre_e[now]].cap);
      }
      ret += f * h[t], flow -= f;
      for (int now = t; now != s; now = pre_v[now]) {
        edge &e = es[pre_v[now]][pre_e[now]];
        e.cap -= f, es[now][e.rev].cap += f;
      }
    }
    return ret;
  }
};

int main() {
  int H, W;
  cin >> H >> W;
  string S[H];
  rep(i, H) cin >> S[i];
  int N = H * W;
  Min_Cost_Flow<ll> G(2 * N + 2);
  int s = 2 * N, t = 2 * N + 1;
  int dx[] = {1, 0}, dy[] = {0, 1};
  int f = 0;
  rep(i, H) {
    rep(j, W) {
      G.add_edge(N + W * i + j, t, 1, 0);
      if (S[i][j] != 'o')
        continue;
      G.add_edge(s, W * i + j, 1, 0);
      f++;
      queue<pii> que;
      que.emplace(i, j);
      while (!que.empty()) {
        int x, y;
        tie(x, y) = que.front();
        que.pop();
        G.add_edge(W * i + j, N + W * x + y, 1, (H + W) - (x + y) + (i + j));
        rep(k, 2) {
          int nx = x + dx[k], ny = y + dy[k];
          if (nx >= H || ny >= W || S[nx][ny] == '#')
            continue;
          que.emplace(nx, ny);
        }
      }
    }
  }
  cout << -G.min_cost_flow(s, t, f) + (H + W) * f << endl;
}

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < n; i++)
#define rep2(i, x, n) for (int i = x; i <= n; i++)
#define rep3(i, x, n) for (int i = x; i >= n; i--)
#define elif else if
#define sp(x) fixed << setprecision(x)
#define pb push_back
#define eb emplace_back
#define all(x) x.begin(), x.end()
#define sz(x) (int)x.size()
using ll = long long;
using pii = pair<int, int>;
using pil = pair<int, ll>;
using pli = pair<ll, int>;
using pll = pair<ll, ll>;
const int MOD = 1000000007;
// const int MOD = 998244353;
const int inf = (1 << 30) - 1;
const ll INF = (1LL << 60) - 1;
const double pi = acos(-1.0);
const double EPS = 1e-10;
template <typename T> bool chmax(T &x, const T &y) {
  return (x < y) ? (x = y, true) : false;
};
template <typename T> bool chmin(T &x, const T &y) {
  return (x > y) ? (x = y, true) : false;
};

template <typename T> struct Min_Cost_Flow {
  struct edge {
    int to;
    T cap, cost;
    int rev;
    edge(int to, T cap, T cost, int rev)
        : to(to), cap(cap), cost(cost), rev(rev) {}
  };

  vector<vector<edge>> es;
  vector<T> d;
  vector<int> pre_v, pre_e;
  const T INF_T;
  const int n;

  Min_Cost_Flow(int n) : INF_T(numeric_limits<T>::max()), n(n) {
    es.resize(n), d.resize(n), pre_v.resize(n), pre_e.resize(n);
  }

  void add_edge(int from, int to, T cap, T cost) {
    es[from].eb(to, cap, cost, sz(es[to]));
    es[to].eb(from, 0, -cost, sz(es[from]) - 1);
  }

  void bellman_ford(int s) {
    fill(all(d), INF_T);
    d[s] = 0;
    while (true) {
      bool update = false;
      rep(i, n) {
        if (d[i] == INF_T)
          continue;
        rep(j, sz(es[i])) {
          edge &e = es[i][j];
          if (e.cap > 0 && chmin(d[e.to], d[i] + e.cost)) {
            pre_v[e.to] = i, pre_e[e.to] = j;
            update = true;
          }
        }
      }
      if (!update)
        break;
    }
  }

  T min_cost_flow(int s, int t, T flow) {
    T ret = 0;
    while (flow > 0) {
      bellman_ford(s);
      if (d[t] == INF_T)
        return -1;
      T f = flow;
      for (int now = t; now != s; now = pre_v[now]) {
        chmin(f, es[pre_v[now]][pre_e[now]].cap);
      }
      ret += f * d[t], flow -= f;
      for (int now = t; now != s; now = pre_v[now]) {
        edge &e = es[pre_v[now]][pre_e[now]];
        e.cap -= f, es[now][e.rev].cap += f;
      }
    }
    return ret;
  }
};

int main() {
  int H, W;
  cin >> H >> W;
  string S[H];
  rep(i, H) cin >> S[i];
  int N = H * W;
  Min_Cost_Flow<ll> G(N + 2);
  int s = N, t = N + 1;
  int dx[] = {1, 0}, dy[] = {0, 1};
  int f = 0;
  rep(i, H) {
    rep(j, W) {
      if (S[i][j] == '#')
        continue;
      G.add_edge(W * i + j, t, 1, 0);
      rep(k, 2) {
        int ni = i + dx[k], nj = j + dy[k];
        if (ni >= H || nj >= W || S[ni][nj] == '#')
          continue;
        G.add_edge(W * i + j, W * ni + nj, INF, -1);
      }
      if (S[i][j] != 'o')
        continue;
      G.add_edge(s, W * i + j, 1, 0);
      f++;
    }
  }
  cout << -G.min_cost_flow(s, t, f) << endl;
}


#include <bits/stdc++.h>
#include <random>

using namespace std;

typedef unsigned long long ull;
typedef long long ll;
typedef double ld;
// #define ll __int128
// #define int ll
// #dLefine char ll
// #define double ld
typedef vector<char> vc;
typedef vector<vc> vvc;
typedef vector<vvc> vvvc;
typedef pair<int, int> pii;
typedef pair<pii, pii> piii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
typedef vector<pii> vpi;
typedef vector<vi> vvi;
typedef vector<vvi> vvvi;
typedef vector<short> vs;
typedef vector<vs> vvs;
typedef vector<vvs> vvvs;
typedef vector<ll> vl;
typedef vector<vl> vvl;
typedef vector<vvl> vvvl;
typedef vector<ld> vld;
typedef vector<vld> vvld;
typedef vector<vvld> vvvld;
typedef vector<string> vst;
typedef vector<vst> vvst;
typedef pair<ld, ld> pld;

#define inmin(a, b) a = min(a, (b))
#define inmax(a, b) a = max(a, (b))
#define ALL(a) a.begin(), a.end()
#define RALL(a) a.rbegin(), a.rend()
#define sqr(x) ((x) * (x))
#define fori(i, n) for (int i = 0; i < int(n); ++i)
#define SZ(a) ((int)((a).size()))
#define triple(T) tuple<T, T, T>
#define quad(T) tuple<T, T, T, T>
#define watch(x) cerr << (#x) << " = " << (x) << endl;

#ifdef MAX_HOME
#define cerr cout
#else
#define cerr                                                                   \\
  if (false)                                                                   \\
  cerr
#endif

const double PI = 2 * acos(0.0);
#define rand shittttty_shit
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
mt19937_64 rng_64(chrono::steady_clock::now().time_since_epoch().count());

const string DIGITS = "0123456789";
const string ALPH = "abcdefghijklmnopqrstuvwxyz";

template <class T0, class T1>
inline ostream &operator<<(ostream &out, pair<T0, T1> &a) {
  return out << "{" << a.first << ", " << a.second << "}";
}

template <class T0, class T1>
inline istream &operator>>(istream &in, pair<T0, T1> &a) {
  return in >> a.first >> a.second;
}

template <class T0, class T1, class T2>
inline ostream &operator<<(ostream &out, tuple<T0, T1, T2> &a) {
  return out << "{" << get<0>(a) << ", " << get<1>(a) << ", " << get<2>(a)
             << "}";
}

template <class T0, class T1, class T2, class T3>
inline ostream &operator<<(ostream &out, tuple<T0, T1, T2, T3> &a) {
  return out << "{" << get<0>(a) << ", " << get<1>(a) << ", " << get<2>(a)
             << ", " << get<3>(a) << "}";
}

template <class T> inline ostream &operator<<(ostream &out, vector<T> &a) {
  out << "[";
  fori(i, a.size()) out << a[i]
                        << vector<string>{", ", "]  "}[i + 1 == a.size()];
  return out;
}

void smain();

signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
#ifdef MAX_HOME
  freopen("input.txt", "r", stdin);
  clock_t start = clock();
#endif
#ifdef HK_CUP
  freopen("finput.txt", "r", stdin);
  freopen("output.txt", "w", stdout);
#endif

  cout << setprecision(12) << fixed;
  smain();
#ifdef MAX_HOME
#ifndef HK_CUP
  cout << "\\n\\nTOTAL EXECUTION TIME: "
       << float(clock() - start) / CLOCKS_PER_SEC << endl;
#endif
#endif
  return 0;
}

template <class flow_t> struct Hungarian {
  flow_t oo;
  Hungarian(flow_t oo) : oo(oo) {}
  flow_t hungarian(vector<vector<flow_t>> a) { /* 1-INDEXED O(n * n * m) */
    int n = (int)a.size() - 1;
    int m = (int)a[0].size() - 1;
    vector<flow_t> u(n + 1), v(m + 1);
    vector<int> p(m + 1), way(m + 1);
    for (int i = 1; i <= n; ++i) {
      p[0] = i;
      int j0 = 0;
      vector<flow_t> minv(m + 1, oo);
      vector<char> used(m + 1, false);
      do {
        used[j0] = true;
        int i0 = p[j0], j1;
        flow_t delta = oo;
        for (int j = 1; j <= m; ++j)
          if (!used[j]) {
            flow_t cur = a[i0][j] - u[i0] - v[j];
            if (cur < minv[j])
              minv[j] = cur, way[j] = j0;
            if (minv[j] < delta)
              delta = minv[j], j1 = j;
          }
        for (int j = 0; j <= m; ++j)
          if (used[j])
            u[p[j]] += delta, v[j] -= delta;
          else
            minv[j] -= delta;
        j0 = j1;
      } while (p[j0] != 0);
      do {
        int j1 = way[j0];
        p[j0] = p[j1];
        j0 = j1;
      } while (j0);
    }
    vector<int> ans(n + 1);
    for (int j = 1; j <= m; ++j)
      ans[p[j]] = j;
    flow_t total_cost = -v[0];
    return total_cost;
  }
};

const int oo = 1e8 + 10;
const int N = 55;

string mat[N];

void smain() {

  int n, m;
  cin >> n >> m;
  fori(i, n) { cin >> mat[i]; }
  vvi cost(n * m + 1, vi(n * m + 1, 0));
  fori(i, n) {
    fori(j, m) {
      if (mat[i][j] == 'o') {
        vvi used(n, vi(m, false));
        used[i][j] = 1;
        for (int ii = i; ii < n; ++ii) {
          for (int jj = j; jj < m; ++jj) {
            if (used[ii][jj]) {
              if (ii + 1 != n && mat[ii + 1][jj] != '#') {
                used[ii + 1][jj] = 1;
              }
              if (jj + 1 != m && mat[ii][jj + 1] != '#') {
                used[ii][jj + 1] = 1;
              }
            }
          }
        }
        fori(ii, n) {
          fori(jj, m) {
            int cs = oo;
            if (used[ii][jj]) {
              cs = -(abs(ii - i) + abs(jj - j));
            }
            cost[i * m + j + 1][ii * m + jj + 1] = cs;
          }
        }
      }
    }
  }

  //    fori (i, cost.size()) {
  //        fori (j, cost.size()) {
  //            cerr << cost[i][j] << ' ';
  //        }
  //        cerr << '\\n';
  //    }
  Hungarian<int> hungarian(oo);
  int ans = hungarian.hungarian(cost);
  cout << -ans;
}

#include <bits/stdc++.h>
#include <random>

using namespace std;

typedef unsigned long long ull;
typedef long long ll;
typedef double ld;
// #define ll __int128
#define int ll
// #dLefine char ll
// #define double ld
typedef vector<char> vc;
typedef vector<vc> vvc;
typedef vector<vvc> vvvc;
typedef pair<int, int> pii;
typedef pair<pii, pii> piii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
typedef vector<pii> vpi;
typedef vector<vi> vvi;
typedef vector<vvi> vvvi;
typedef vector<short> vs;
typedef vector<vs> vvs;
typedef vector<vvs> vvvs;
typedef vector<ll> vl;
typedef vector<vl> vvl;
typedef vector<vvl> vvvl;
typedef vector<ld> vld;
typedef vector<vld> vvld;
typedef vector<vvld> vvvld;
typedef vector<string> vst;
typedef vector<vst> vvst;
typedef pair<ld, ld> pld;

#define inmin(a, b) a = min(a, (b))
#define inmax(a, b) a = max(a, (b))
#define ALL(a) a.begin(), a.end()
#define RALL(a) a.rbegin(), a.rend()
#define sqr(x) ((x) * (x))
#define fori(i, n) for (int i = 0; i < int(n); ++i)
#define SZ(a) ((int)((a).size()))
#define triple(T) tuple<T, T, T>
#define quad(T) tuple<T, T, T, T>
#define watch(x) cerr << (#x) << " = " << (x) << endl;

#ifdef MAX_HOME
#define cerr cout
#else
#define cerr                                                                   \\
  if (false)                                                                   \\
  cerr
#endif

const double PI = 2 * acos(0.0);
#define rand shittttty_shit
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
mt19937_64 rng_64(chrono::steady_clock::now().time_since_epoch().count());

const string DIGITS = "0123456789";
const string ALPH = "abcdefghijklmnopqrstuvwxyz";

template <class T0, class T1>
inline ostream &operator<<(ostream &out, pair<T0, T1> &a) {
  return out << "{" << a.first << ", " << a.second << "}";
}

template <class T0, class T1>
inline istream &operator>>(istream &in, pair<T0, T1> &a) {
  return in >> a.first >> a.second;
}

template <class T0, class T1, class T2>
inline ostream &operator<<(ostream &out, tuple<T0, T1, T2> &a) {
  return out << "{" << get<0>(a) << ", " << get<1>(a) << ", " << get<2>(a)
             << "}";
}

template <class T0, class T1, class T2, class T3>
inline ostream &operator<<(ostream &out, tuple<T0, T1, T2, T3> &a) {
  return out << "{" << get<0>(a) << ", " << get<1>(a) << ", " << get<2>(a)
             << ", " << get<3>(a) << "}";
}

template <class T> inline ostream &operator<<(ostream &out, vector<T> &a) {
  out << "[";
  fori(i, a.size()) out << a[i]
                        << vector<string>{", ", "]  "}[i + 1 == a.size()];
  return out;
}

void smain();

signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
#ifdef MAX_HOME
  freopen("input.txt", "r", stdin);
  clock_t start = clock();
#endif
#ifdef HK_CUP
  freopen("finput.txt", "r", stdin);
  freopen("output.txt", "w", stdout);
#endif

  cout << setprecision(12) << fixed;
  smain();
#ifdef MAX_HOME
#ifndef HK_CUP
  cout << "\\n\\nTOTAL EXECUTION TIME: "
       << float(clock() - start) / CLOCKS_PER_SEC << endl;
#endif
#endif
  return 0;
}

const ll oo = 1e12 + 10;

template <int N> struct Mcmf {
  struct edge {
    int from, to, flow, cap, cost;
  };
  vector<edge> e;
  vi g[N];

  inline void add_edge(int from, int to, int cap, int cost) {
    //        cout << from << " , " << to << " = (" << cap << ", " << cost <<
    //        ")" << endl;
    g[from].push_back(e.size());
    e.push_back({from, to, 0, cap, cost});
    g[to].push_back(e.size());
    e.push_back({to, from, 0, 0, -cost});
  }

  int d[N];
  bool in_que[N];
  int from[N];

  pii mcmf(int s, int t) {
    int flow = 0, cost = 0;
    while (1) {
      fill(d, d + t + 1, oo);
      fill(in_que, in_que + t + 1, 0);
      d[s] = 0;
      in_que[s] = 1;
      queue<int> q;
      q.push(s);
      while (!q.empty()) {
        int v = q.front();
        q.pop();
        in_que[v] = 0;
        for (int id : g[v]) {
          int to = e[id].to;
          if (e[id].cap > e[id].flow && d[to] > d[v] + e[id].cost) {
            d[to] = d[v] + e[id].cost;
            from[to] = id;
            if (!in_que[to]) {
              q.push(to);
              in_que[to] = 1;
            }
          }
        }
      }
      if (d[t] == oo)
        break;
      int v = t;
      vi ids;
      int pushed = oo;
      while (v != s) {
        int id = from[v];
        ids.push_back(id);
        v = e[id].from;
        inmin(pushed, e[id].cap - e[id].flow);
      }
      flow += pushed;
      for (auto id : ids) {
        e[id].flow += pushed;
        e[id ^ 1].flow -= pushed;
        cost += pushed * e[id].cost;
      }
    }
    return {flow, cost};
  };
};

const int oosmall = 1e6 + 10;
const int N = 55;

string mat[N];

const int S = 1e4 + 10;

void smain() {

  int n, m;
  cin >> n >> m;
  fori(i, n) { cin >> mat[i]; }
  Mcmf<S> mcmf;

  int s = n * m;
  int t = s + 1;
  int cnt0 = 0;
  fori(i, n) {
    fori(j, m) {
      mcmf.add_edge(i * m + j, t, 1, 0);
      if (mat[i][j] == 'o') {
        cnt0++;
        mcmf.add_edge(s, i * m + j, 1, -oosmall);
      }
      if (i + 1 != n && mat[i][j] != '#' && mat[i + 1][j] != '#') {
        mcmf.add_edge(i * m + j, (i + 1) * m + j, oosmall, -1);
      }
      if (j + 1 != m && mat[i][j] != '#' && mat[i][j + 1] != '#') {
        mcmf.add_edge(i * m + j, i * m + j + 1, oosmall, -1);
      }
    }
  }

  pii ans = mcmf.mcmf(s, t);
  watch(ans);
  cout << -(ans.second + oosmall * cnt0);
}