#include <bits/stdc++.h>

#define F first
#define S second
#define prev azaza
#define MP make_pair
#define PB push_back

using namespace std;
typedef long long ll;
typedef long double ld;

const int max_n = 51, max_f = 105, inf = 1000111222;

char f[max_n][max_n];

int n,m;
bool vis[max_n][max_n];
vector<pair<int, int>> p;
vector<pair<int, int>> v[max_f];

bool onf(int i, int j) {
    return i < n && j < m && f[i][j] != '#';
}

void dfs(int i, int j, int ind, int d) {
    v[ind].emplace_back(d, i * m + j);
    vis[i][j] = 1;
    if (onf(i + 1, j) && !vis[i + 1][j]) {
        dfs(i + 1, j, ind, d + 1);
    }
    if (onf(i, j + 1) && !vis[i][j + 1]) {
        dfs(i, j + 1, ind, d + 1);
    }
}


namespace min_cost_max_flow {

const int max_v = 2651;
const int DIJKSTRA_N2 = 1;
const int DIJKSTRA_MLOGN = 2;
const int DIJKSTRA_MAXD = 3;
const int DIJKSTRA_TYPE = 2;
const bool NEGATIVE_COST_INITIAL = true;
const bool ACYCLIC_INITIAL = true;

struct edge {
    int v, residual_capacity, cost;

    edge(int v, int residual_capacity, int cost): v(v), residual_capacity(residual_capacity), cost(cost) {
    }
};

vector<edge> edges;
vector<int> g[max_v];

void add_edge(int u, int v, int f, int c) {
    //cout << u << ' ' << v << ' ' << f << endl;
    g[u].push_back(edges.size());
    edges.push_back({v, f, c});
    g[v].push_back(edges.size());
    edges.push_back({u, 0, -c});
}

int phi[max_v], dist[max_v];
char used[max_v];

bool dijkstra_mlogn(int s, int t) {
    for (int i = 0; i <= t; ++i) {
        dist[i] = inf;
    }
    dist[s] = 0;
    priority_queue<pair<int, int>> q;
    q.push({0, s});
    while (!q.empty()) {
        pair<int, int> p = q.top();
        q.pop();
        const int v = p.second;
        if (-p.first != dist[v]) {
            continue;
        }
        //cout << v << ": " << dist[v] << ", phi = " << phi[v] << endl;
        for (int id : g[v]) {
            if (edges[id].residual_capacity == 0) {
                continue;
            }
            const int to = edges[id].v;
            if (dist[v] + phi[v] - phi[to] + edges[id].cost < dist[to]) {
                dist[to] = dist[v] + phi[v] - phi[to] + edges[id].cost;
                q.push({-dist[to], to});
            }
        }
    }
    for (int i = 0; i <= t; ++i) {
        dist[i] = min(inf, dist[i] + phi[i]);
        phi[i] = dist[i];
    }
    return dist[t] != inf;
}

const int MAX_D = 0;

vector<int> all_v[MAX_D];

bool dijkstra_max_d(int s, int t) {
    for (int i = 0; i <= t; ++i) {
        dist[i] = inf;
    }
    dist[s] = 0;
    int curd = 0;
    all_v[0].push_back(s);
    while (true) {
        while (curd < MAX_D && all_v[curd].empty()) {
            ++curd;
        }
        if (curd == MAX_D) {
            break;
        }
        const int v = all_v[curd].back();
        all_v[curd].pop_back();
        if (curd != dist[v]) {
            continue;
        }
        for (int id : g[v]) {
            if (edges[id].residual_capacity == 0) {
                continue;
            }
            const int to = edges[id].v;
            if (dist[v] + phi[v] - phi[to] + edges[id].cost < dist[to]) {
                dist[to] = dist[v] + phi[v] - phi[to] + edges[id].cost;
                assert(dist[to] < MAX_D);
                all_v[dist[to]].push_back(to);
            }
        }
    }
    for (int i = 0; i <= t; ++i) {
        dist[i] = min(inf, dist[i] + phi[i]);
        phi[i] = dist[i];
    }
    return dist[t] != inf;
}

bool dijkstra_n2(int s, int t) {
    for (int i = 0; i <= t; ++i) {
        dist[i] = inf;
    }
    dist[s] = 0;
    for (int i = 0; i <= t; ++i) {
        int v = -1;
        for (int j = 0; j <= t; ++j) {
            if (!used[j] && (v == -1 || dist[j] < dist[v])) {
                v = j;
            }
        }
        used[v] = 1;
        for (int id : g[v]) {
            if (edges[id].residual_capacity == 0) {
                continue;
            }
            const int to = edges[id].v;
            if (dist[v] + phi[v] - phi[to] + edges[id].cost < dist[to]) {
                dist[to] = dist[v] + phi[v] - phi[to] + edges[id].cost;
            }
        }
    }
    for (int i = 0; i <= t; ++i) {
        dist[i] = min(inf, dist[i] + phi[i]);
        phi[i] = dist[i];
        used[i] = 0;
    }
    return dist[t] != inf;
}

bool dijkstra(int s, int t) {
    if (DIJKSTRA_TYPE == 0) {
        puts("Select dijkstra type");
        exit(1);
    }
    if (DIJKSTRA_TYPE == DIJKSTRA_N2) {
        return dijkstra_n2(s, t);
    } else if (DIJKSTRA_TYPE == DIJKSTRA_MLOGN) {
        return dijkstra_mlogn(s, t);
    } else if (DIJKSTRA_TYPE == DIJKSTRA_MAXD) {
        return dijkstra_max_d(s, t);
    }
    puts("Unsupported dijkstra type");
    exit(2);
}

int start[max_v];

int dfs(int v, int t, int f) {
    if (v == t) {
        return f;
    }
    used[v] = 1;
    for (int &i = start[v]; i < g[v].size(); ++i) {
        const int id = g[v][i];
        if (edges[id].residual_capacity && dist[v] + edges[id].cost == dist[edges[id].v] && !used[edges[id].v]) {
            int res = dfs(edges[id].v, t, min(f, edges[id].residual_capacity));
            if (res) {
                edges[id].residual_capacity -= res;
                edges[id ^ 1].residual_capacity += res;
                used[v] = 0;
                return res;
            }
        }
    }
    used[v] = 0;
    return 0;
}

void init_phi(int v, int s) {
    if (v == s) {
        phi[v] = 0;
        return;
    }
    if (phi[v] != inf) {
        return;
    }
    for (int id : g[v]) {
        if (edges[id].residual_capacity != 0) {
            continue;
        }
        init_phi(edges[id].v, s);
        phi[v] = min(phi[v], phi[edges[id].v] - edges[id].cost);
    }
}

long long min_cost_max_flow(int s, int t) {
    assert(t < max_v);
    if (!NEGATIVE_COST_INITIAL) {
        fill(phi, phi + t + 1, 0);
    } else if (ACYCLIC_INITIAL) {
        fill(phi, phi + t + 1, inf);
        init_phi(t, s);
    } else {
        cout << "FAIL, graph should be either acyclic, or doesn't contain negative edges" << endl;
        exit(1);
    }
    long long res = 0;
    while (dijkstra(s, t)) {
        memset(start, 0, sizeof(int) * (t + 1));
        while (int x = dfs(s, t, inf)) {
            res += 1LL * x * dist[t];
        }
    }
    return res;
}

void clr(int t) {
    edges.clear();
    for (int i = 0; i <= t; ++i) {
        g[i].clear();
        phi[i] = dist[i] = used[i] = 0;
    }
}

}

int main()
{
    //freopen("input.txt", "r", stdin);
    cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        scanf("%s", f[i]);
    }
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            if (f[i][j] == 'o') {
                int ind = p.size();
                p.emplace_back(i, j);
                for (int i = 0; i < n; ++i) {
                    memset(vis[i], 0, sizeof(vis[i]));
                }
                dfs(i, j, ind, 0);
            }
        }
    }
    for (int i = 0; i < p.size(); ++i) {
        sort(v[i].begin(), v[i].end());
        reverse(v[i].begin(), v[i].end());
        while(v[i].size() > p.size()) {
            v[i].pop_back();
        }
    }
    int s = p.size() + n * m;
    int t = s + 1;
    for (int i = 0; i < p.size(); ++i) {
        for (int j = 0; j < v[i].size(); ++j) {
            //cout << v[i][j].S << ' ' << v[i][j].F << endl;
            min_cost_max_flow::add_edge(i, v[i][j].S + p.size(), 1, -v[i][j].F);
        }
    }
    for (int i = 0; i  <p.size(); ++i) {
        min_cost_max_flow::add_edge(s, i, 1, 0);
    }
    for (int i = 0; i < n * m; ++i) {
        min_cost_max_flow::add_edge(p.size() + i, t, 1, 0);
    }
    cout << -min_cost_max_flow::min_cost_max_flow(s, t) << endl;


    return 0;
}




Measured memory_bytes: 4784