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

#pragma GCC optimize("Ofast,unroll-loops")
#pragma GCC target("avx,avx2,fma")

typedef long long LL;
typedef pair<int, int> PII;
typedef vector<int> VI;
#define MP make_pair
#define PB push_back
#define X first
#define Y second

#define FOR(i, a, b) for(int i = (a); i < (b); ++i)
#define RFOR(i, b, a) for(int i = (b) - 1; i >= (a); --i)
#define ALL(a) a.begin(), a.end()
#define SZ(a) (int)((a).size())
#define FILL(a, value) memset(a, value, sizeof(a))
#define debug(a) cerr << #a << " = " << a << endl;

template<typename T> void setmax(T& x, T y) {x = max(x, y);}
template<typename T> void setmin(T& x, T y) {x = min(x, y);}

template<typename T> void print(const T& a, ostream& out){	
	for(auto i: a) out << i << ' ';
	out << endl;
}

const double PI = acos(-1.0);
const LL INF = 1e9 + 47;
const LL LINF = INF * INF;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template<typename flow_t, typename cost_t>
struct min_cost_flow {
	const cost_t COST_INF = numeric_limits<cost_t>::max() / 2;
 
	struct edge {
		int node, rev;
		flow_t capacity;
		cost_t cost;
 
		edge() {}
 
		edge(int _node, int _rev, flow_t _capacity, cost_t _cost)
			: node(_node), rev(_rev), capacity(_capacity), cost(_cost) {}
	};
 
	int V = -1, E = 0;
	vector<vector<edge>> adj;
	vector<cost_t> dist;
	vector<int> prev;
	vector<edge*> prev_edge;
	bool too_many_bellman_ford_iterations = false;
 
	min_cost_flow(int vertices = -1) {
		if (vertices >= 0)
			init(vertices);
	}
 
	void init(int vertices) {
		V = vertices;
		E = 0;
		adj.assign(V, {});
		dist.resize(V);
		prev.resize(V);
		prev_edge.resize(V);
		too_many_bellman_ford_iterations = false;
	}
 
	void add_directional_edge(int u, int v, flow_t capacity, cost_t cost) {
		assert(0 <= u && u < V && 0 <= v && v < V);
		edge uv_edge(v, adj[v].size() + (u == v ? 1 : 0), capacity, cost);
		edge vu_edge(u, adj[u].size(), 0, -cost);
		adj[u].push_back(uv_edge);
		adj[v].push_back(vu_edge);
		E++;
	}
 
	edge &reverse_edge(const edge &e) {
		return adj[e.node][e.rev];
	}
 
	bool bellman_ford(int source, int sink) {
		for (int i = 0; i < V; i++) {
			dist[i] = COST_INF;
			prev[i] = -1;
			prev_edge[i] = nullptr;
		}
 
		long long work = 0;
		vector<int> last_seen(V, -1);
		vector<int> nodes = {source}, next_nodes;
		dist[source] = 0;
 
		for (int iteration = 0; iteration < V; iteration++) {
			next_nodes.clear();
 
			for (int node : nodes) {
				for (edge &e : adj[node])
					if (e.capacity > 0 && dist[node] + e.cost < dist[e.node]) {
						dist[e.node] = dist[node] + e.cost;
						prev[e.node] = node;
						prev_edge[e.node] = &e;
 
						if (last_seen[e.node] != iteration) {
							last_seen[e.node] = iteration;
							next_nodes.push_back(e.node);
						}
					}
 
				work += adj[node].size();
			}
 
			swap(nodes, next_nodes);
		}
 
		if (work > 1.75 * E * (32 - __builtin_clz(V)) + 100)
			too_many_bellman_ford_iterations = true;
 
		return prev[sink] != -1;
	}
 
	struct dijkstra_state {
		cost_t dist;
		int node;
 
		dijkstra_state() {}
 
		dijkstra_state(cost_t _dist, int _node) : dist(_dist), node(_node) {}
 
		bool operator<(const dijkstra_state &other) const {
			return dist > other.dist;
		}
	};
 
	void dijkstra_check(int node, cost_t potential_dist, int previous, edge *previous_edge,
						priority_queue<dijkstra_state> &pq) {
		if (potential_dist < dist[node]) {
			dist[node] = potential_dist;
			prev[node] = previous;
			prev_edge[node] = previous_edge;
			pq.emplace(dist[node], node);
		}
	}
 
	bool dijkstra(int source, int sink) {
		dist.assign(V, COST_INF);
		prev.assign(V, -1);
		prev_edge.assign(V, nullptr);
 
		priority_queue<dijkstra_state> pq;
		dijkstra_check(source, 0, -1, nullptr, pq);
 
		while (!pq.empty()) {
			dijkstra_state top = pq.top();
			pq.pop();
 
			if (top.dist > dist[top.node])
				continue;
 
			for (edge &e : adj[top.node])
				if (e.capacity > 0)
					dijkstra_check(e.node, top.dist + e.cost, top.node, &e, pq);
		}
 
		return prev[sink] != -1;
	}
 
	void reduce_cost() {
		for (int i = 0; i < V; i++)
			for (edge &e : adj[i])
				if (dist[i] < COST_INF && dist[e.node] < COST_INF)
					e.cost += dist[i] - dist[e.node];
	}
 
	vector<pair<flow_t, cost_t>> options;
 
	pair<flow_t, cost_t> solve_min_cost_flow(int source, int sink, flow_t flow_goal = numeric_limits<flow_t>::max()) {
		assert(V >= 0);
		flow_t total_flow = 0;
		cost_t total_cost = 0;
 
		while (total_flow < flow_goal && bellman_ford(source, sink)) {
			if (too_many_bellman_ford_iterations)
				break;
 
			flow_t path_cap = flow_goal - total_flow;
			cost_t cost_sum = 0;
 
			for (int node = sink; prev[node] != -1; node = prev[node])
				path_cap = min(path_cap, prev_edge[node]->capacity);
 
			for (int node = sink; prev[node] != -1; node = prev[node]) {
				edge *e = prev_edge[node];
				e->capacity -= path_cap;
				reverse_edge(*e).capacity += path_cap;
				cost_sum += e->cost;
			}
 
			total_flow += path_cap;
			total_cost += cost_sum * path_cap;
			options.emplace_back(total_flow, total_cost);
		}
 
		if (too_many_bellman_ford_iterations) {
			cost_t reduce_sum = 0;
 
			do {
				reduce_cost();
				reduce_sum += dist[sink];
				flow_t path_cap = flow_goal - total_flow;
 
				for (int node = sink; prev[node] != -1; node = prev[node])
					path_cap = min(path_cap, prev_edge[node]->capacity);
 
				for (int node = sink; prev[node] != -1; node = prev[node]) {
					edge *e = prev_edge[node];
					assert(e->cost == 0);
					e->capacity -= path_cap;
					reverse_edge(*e).capacity += path_cap;
				}
 
				total_flow += path_cap;
				total_cost += reduce_sum * path_cap;
				options.emplace_back(total_flow, total_cost);
			} while (total_flow < flow_goal && dijkstra(source, sink));
		}
 
		return make_pair(total_flow, total_cost);
	}
};

int main()
{
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);

	int n, m; cin >> n >> m;
	vector<string> s(n);
	FOR(i, 0, n) cin >> s[i];

	vector<vector<int>> id(n, vector<int>(m, -1));
	int ptr = 0, ptr2 = 0;
	FOR(i, 0, n) FOR(j, 0, m) if (s[i][j] != '#'){
		id[i][j] = ptr++;
		if (s[i][j] == 'o') ptr2++;
	}

	auto ok = [&](int x, int y){
		return x >= 0 && x < n && y >= 0 && y < m && s[x][y] != '#';
	};

	const int sz = ptr + ptr2;
	min_cost_flow<int, int> mcmf(sz + 2);
	int P = ptr2;
	ptr2 = 0;
	
	FOR(i, 0, n) FOR(j, 0, m) if (s[i][j] == 'o'){
		mcmf.add_directional_edge(sz, ptr2, 1, 0);
		vector<vector<int>> used(n, vector<int>(m, 0));
		
		function<void(int, int)> dfs = [&](int x, int y){
			if (used[x][y]) return;
			used[x][y] = 1;
			if (ok(x + 1, y)) dfs(x + 1, y);
			if (ok(x, y + 1)) dfs(x, y + 1);
		};

		dfs(i, j);
		FOR(x, 0, n) FOR(y, 0, m) {
			if (id[x][y] != -1 && used[x][y]){
				mcmf.add_directional_edge(ptr2, P + id[x][y], 1, -(x - i + y - j));
			}
		}
		
		ptr2++;
	}

	FOR(i, 0, n) FOR(j, 0, m) if (id[i][j] != -1){
		mcmf.add_directional_edge(P + id[i][j], sz + 1, 1, 0);
	}

	auto res = mcmf.solve_min_cost_flow(sz, sz + 1);
	//cerr << res.X << endl;
	cout << -res.Y << endl;
	
   // cerr << "Time elapsed: " << clock() / (double)CLOCKS_PER_SEC << endl;
    return 0;
}


Measured memory_bytes: 6608