#include "iostream"
#include "climits"
#include "list"
#include "queue"
#include "stack"
#include "set"
#include "functional"
#include "algorithm"
#include "string"
#include "map"
#include "unordered_map"
#include "unordered_set"
#include "iomanip"
#include "cmath"
#include "random"
#include "bitset"
#include "cstdio"
#include "numeric"
#include "cassert"
#include "ctime"

using namespace std;

constexpr long long int MOD = 1000000007;
//constexpr int MOD = 1000000007;
//constexpr int MOD = 998244353;
//constexpr long long int MOD = 998244353;
constexpr double EPS = 1e-12;

//int N, M, K, T, H, W, L, R;
long long int N, M, K, T, H, W, L, R;

struct Edge {
	int to;
	long long int capa;
	long long int cost;
	unsigned long long int rev_index;
};

class Min_Cost_Flow {
	int n;
	bool d;
	vector<vector<Edge>>edge;
	vector<long long int>potent;
	vector<long long int>dist;
	vector<int>pre_node;
	vector<int>pre_edge;
public:
	Min_Cost_Flow(int node, bool directed) {
		node += 2;
		n = node;
		if (directed)d = true;
		else d = false;
		edge.resize(n);
		potent.resize(n);
		dist.resize(n);
		pre_node.resize(n);
		pre_edge.resize(n);
		return;
	}
	void Add_Edge(int s, int g, long long int cap, long long int cost) {
		if (!d) {
			edge[s].push_back({ g,cap,cost,edge[g].size() });
			edge[g].push_back({ s,0,-cost ,edge[s].size() - 1 });
		}
		else {////Fix!Fix!Fix!!!!!!!
			edge[s].push_back({ g,cap,cost,edge[g].size() });
			edge[g].push_back({ s,cap,cost ,edge[s].size() - 1 });
		}
	}
	long long int Solve(int s, int g, long long int f) {
		long long int ret = 0;
		for (auto &i : potent)i = 0;
		while (f > 0) {
			priority_queue<pair<long long int, int>, vector<pair<long long int, int>>, greater<pair<long long int, int>>>PQ;
			for (auto &i : dist)i = MOD * MOD;
			dist[s] = 0;
			PQ.push({ dist[s],s });
			while (!PQ.empty()) {
				long long int c = PQ.top().first;
				int cn = PQ.top().second;
				PQ.pop();
				if (dist[cn] < c)continue;
				int cnt = 0;
				for (auto &i : edge[cn]) {
					if (i.capa > 0 && dist[i.to] > dist[cn] + i.cost + potent[cn] - potent[i.to]) {
						dist[i.to] = dist[cn] + i.cost + potent[cn] - potent[i.to];
						pre_node[i.to] = cn;
						pre_edge[i.to] = cnt;
						PQ.push({ dist[i.to],i.to });
					}
					cnt++;
				}
			}
			if (dist[g] == MOD * MOD)return -1;
			for (int i = 0; i < n; i++) potent[i] += dist[i];
			long long int d = f;
			for (int i = g; i != s; i = pre_node[i]) {
				d = min(d, edge[pre_node[i]][pre_edge[i]].capa);
			}
			f -= d;
			ret += d * potent[g];
			for (int i = g; i != s; i = pre_node[i]) {
				auto &e = edge[pre_node[i]][pre_edge[i]];
				e.capa -= d;
				edge[i][e.rev_index].capa += d;
			}
		}
		return ret;
	}
};

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

	cin >> H >> W;
	vector<string>s(H);
	for (auto &i : s)cin >> i;
	int num = 0;
	Min_Cost_Flow f(H*W + 2, false);
	for (int i = 0; i < H; i++) {
		for (int j = 0; j < W; j++) {
			if (s[i][j] == 'o') {
				f.Add_Edge(H * W, i*W + j, 1, 0);
				K++;
				num += H + W - i - j;
			}
			if (s[i][j] == '#')continue;
			if (i + 1 < H&&s[i + 1][j] != '#') {
				f.Add_Edge(i*W + j, (i + 1)*W + j, H*W, 0);
			}
			if (j + 1 < W&&s[i][j + 1] != '#') {
				f.Add_Edge(i*W + j, (i)*W + j + 1, H*W, 0);
			}
			f.Add_Edge(i*W + j, H*W + 1, 1, H + W - i - j);
		}
	}
	cout << num - f.Solve(H * W, H * W + 1, K) << endl;
}

Measured memory_bytes: 4368