code = r'''
# distutils: language=c++
# distutils: include_dirs=[/home/contestant/.local/lib/python3.8/site-packages/numpy/core/include,/opt/atcoder-stl]
# cython: boundscheck=False
# cython: wraparound=False
from libcpp cimport bool
from libcpp.string cimport string
from libcpp.vector cimport vector
from libcpp.pair cimport pair
cdef extern from *:
    ctypedef int MOD '998244353'
    ctypedef long long ll 'long long'
#
cdef extern from '<atcoder/fenwicktree>' namespace 'atcoder':
    cdef cppclass fenwick_tree[T]:
        fenwick_tree(int n)
        void add(int p, T x)
        T sum(int l, int r)
cdef class fenwick_tree_:
    cdef fenwick_tree[int] *ptr
    def __cinit__(self, int n): self.ptr = new fenwick_tree[int](n)  
    def __dealloc__(self): del self.ptr
    cpdef void add(self, int p, ll x): self.ptr.add(p, x)
    cpdef ll sum(self, int l, int r): return self.ptr.sum(l, r)
#
cdef extern from '<atcoder/string>' namespace 'atcoder':
    vector[int] suffix_array(vector[int] s, int upper)
    vector[int] suffix_array(vector[ll] s)
    vector[int] suffix_array(string s)
    vector[int] lcp_array(vector[ll] s, vector[int] sa)
    vector[int] lcp_array(string s, vector[int] sa)
    vector[int] z_algorithm(vector[ll] s)
    vector[int] z_algorithm(string s)
cpdef vector[int] suffix_array_u(vector[int] s, int upper): return suffix_array(s, upper)
cpdef vector[int] suffix_array_l(vector[ll] s): return suffix_array(s)
cpdef vector[int] suffix_array_s(string s): return suffix_array(s)
cpdef vector[int] lcp_array_l(vector[ll] s, vector[int] sa): return lcp_array(s, sa)
cpdef vector[int] lcp_array_s(string s, vector[int] sa): return lcp_array(s, sa)
cpdef vector[int] z_algorithm_l(vector[ll] s): return z_algorithm(s)
cpdef vector[int] z_algorithm_s(string s): return z_algorithm(s)
#
cdef extern from '<atcoder/math>' namespace 'atcoder':
    ll pow_mod(ll x, ll n, ll m)
    ll inv_mod(ll x, ll m)
    pair[ll, ll] crt(vector[ll] r, vector[ll] m)
    ll floor_sum(ll n, ll m, ll a, ll b)
cpdef ll pow_mod_(ll x, ll n, ll m): return pow_mod(x, n, m)
cpdef ll inv_mod_(ll x, ll m): return inv_mod(x, m)
cpdef pair[ll, ll] crt_(vector[ll] r, vector[ll] m): return crt(r, m)
cpdef ll floor_sum_(ll n, ll m, ll a, ll b): return floor_sum(n, m, a, b)
#
cdef extern from '<atcoder/convolution>' namespace 'atcoder':
    vector[ll] convolution[m](vector[ll] a, vector[ll] b)
    vector[ll] convolution_ll(vector[ll] a, vector[ll] b)
cpdef vector[ll] convolution_(vector[ll] a, vector[ll] b): return convolution[MOD](a, b)
cpdef vector[ll] convolution_ll_(vector[ll] a, vector[ll] b): return convolution_ll(a, b)
#
cdef extern from '<atcoder/dsu>' namespace 'atcoder':
    cpdef cppclass dsu:
        dsu(int n)
        int merge(int a, int b)
        bool same(int a, int b)
        int leader(int a)
        int size(int a)
        vector[vector[int]] groups()
cdef class dsu_:
    cdef dsu *ptr
    def __cinit__(self, int n): self.ptr = new dsu(n)
    def __dealloc__(self): del self.ptr
    cpdef int merge(self,a,b): return self.ptr.merge(a,b)
    cpdef bool same(self,a,b): return self.ptr.same(a,b)
    cpdef int leader(self,a): return self.ptr.leader(a)
    cpdef int size(self,a): return self.ptr.size(a)
    cpdef vector[vector[int]] groups(self): return self.ptr.groups()
#
cdef extern from '<atcoder/maxflow>' namespace 'atcoder':
    cdef cppclass mf_graph[Cap]:
        mf_graph(int n)
        int add_edge(int frm, int to, Cap cap)
        Cap flow(int s, int t)
        Cap flow(int s, int t, Cap flow_limit)
        vector[bool] min_cut(int s)
        cppclass edge:
            int frm 'from', to
            Cap cap, flow
            edge(edge &e)
        edge get_edge(int i)
        vector[edge] edges()
        void change_edge(int i, Cap new_cap, Cap new_flow)
cdef class mf_graph_:
    cdef mf_graph[ll] *ptr
    def __cinit__(self, int n): self.ptr = new mf_graph[ll](n)  
    def __dealloc__(self): del self.ptr
    cpdef int add_edge(self, int frm, int to, ll cap): return self.ptr.add_edge(frm, to, cap)
    cpdef ll flow(self, int s, int t): return self.ptr.flow(s, t)
    cpdef ll flow_limit(self, int s, int t, ll flow_limit): return self.ptr.flow(s, t, flow_limit)
    cpdef vector[bool] min_cut(self, int s): return self.ptr.min_cut(s)
    cpdef vector[ll] get_edge(self, int i):
        cdef mf_graph[ll].edge *e = new mf_graph[ll].edge(self.ptr.get_edge(i))
        cdef vector[ll] *ret_e = new vector[ll]()		
        ret_e.push_back(e.frm)
        ret_e.push_back(e.to)
        ret_e.push_back(e.cap)
        ret_e.push_back(e.flow)
        return ret_e[0]
    cpdef vector[vector[ll]] edges(self):
        cdef vector[mf_graph[ll].edge] es = self.ptr.edges()
        cdef vector[vector[ll]] *ret_es = new vector[vector[ll]](es.size())
        for i in range(es.size()):
            ret_es.at(i).push_back(es.at(i).frm)
            ret_es.at(i).push_back(es.at(i).to)
            ret_es.at(i).push_back(es.at(i).cap)
            ret_es.at(i).push_back(es.at(i).flow)
        return ret_es[0]
    cpdef void change_edge(self, int i, ll new_cap, ll new_flow): self.ptr.change_edge(i, new_cap, new_flow)
#
cdef extern from '<atcoder/mincostflow>' namespace 'atcoder':
    cdef cppclass mcf_graph[Cap, Cost]:
        mcf_graph(int n)
        int add_edge(int frm, int to, Cap cap, Cost cost)
        pair[Cap, Cost] flow(int s, int t)
        pair[Cap, Cost] flow(int s, int t, Cap flow_limit)
        vector[pair[Cap, Cost]] slope(int s, int t)
        vector[pair[Cap, Cost]] slope(int s, int t, Cap flow_limit)
        cppclass edge:
            int frm 'from', to
            Cap cap, flow
            Cost cost
            edge(edge &e)
        edge get_edge(int i)
        vector[edge] edges()
cdef class mcf_graph_:
    cdef mcf_graph[ll, ll] *ptr
    def __cinit__(self, int n): self.ptr = new mcf_graph[ll, ll](n)  
    def __dealloc__(self): del self.ptr
    cpdef int add_edge(self, int frm, int to, ll cap, ll cost): return self.ptr.add_edge(frm, to, cap, cost)
    cpdef pair[ll, ll] flow(self, int s, int t): return self.ptr.flow(s, t)
    cpdef pair[ll, ll] flow_limit(self, int s, int t, ll flow_limit): return self.ptr.flow(s, t, flow_limit)
    cpdef vector[pair[ll, ll]] slope(self, int s, int t): return self.ptr.slope(s, t)
    cpdef vector[pair[ll, ll]] slope_limit(self, int s, int t, ll flow_limit): return self.ptr.slope(s, t, flow_limit)
    cpdef vector[ll] get_edge(self, int i):
        cdef mcf_graph[ll, ll].edge *e = new mcf_graph[ll, ll].edge(self.ptr.get_edge(i))
        cdef vector[ll] *ret_e = new vector[ll]()
        ret_e.push_back(e.frm)
        ret_e.push_back(e.to)
        ret_e.push_back(e.cap)
        ret_e.push_back(e.flow)
        ret_e.push_back(e.cost)
        return ret_e[0]
    cpdef vector[vector[ll]] edges(self):
        cdef vector[mcf_graph[ll, ll].edge] es = self.ptr.edges()
        cdef vector[vector[ll]] *ret_es = new vector[vector[ll]](es.size())
        for i in range(es.size()):
            ret_es.at(i).push_back(es.at(i).frm)
            ret_es.at(i).push_back(es.at(i).to)
            ret_es.at(i).push_back(es.at(i).cap)
            ret_es.at(i).push_back(es.at(i).flow)
            ret_es.at(i).push_back(es.at(i).cost)
        return ret_es[0]
#
cdef extern from '<atcoder/scc>' namespace 'atcoder':
    cdef cppclass scc_graph:
        scc_graph(int n)
        void add_edge(int fr, int to)
        vector[vector[int]] scc()
cdef class scc_graph_:
    cdef scc_graph *ptr
    def __cinit__(self, int n): self.ptr = new scc_graph(n)  
    def __dealloc__(self): del self.ptr
    cpdef void add_edge(self, int fr, int to): self.ptr.add_edge(fr, to)
    cpdef vector[vector[int]] scc(self): return self.ptr.scc()
#
cdef extern from '<atcoder/twosat>' namespace 'atcoder':
    cdef cppclass two_sat:
        two_sat(int n)
        void add_clause(int i, bool f, int j, bool g)
        bool satisfiable()
        vector[bool] answer()
cdef class two_sat_:
    cdef two_sat *ptr
    def __cinit__(self, int n): self.ptr = new two_sat(n)
    def __dealloc__(self): del self.ptr
    cpdef void add_clause(self, int i, bool f, int j, bool g): self.ptr.add_clause(i, f, j, g)
    cpdef bool satisfiable(self): return self.ptr.satisfiable()
    cpdef vector[bool] answer(self): return self.ptr.answer()
'''
import os
try:
    from atcoder import *
except:
    open('atcoder.pyx', 'w').write(code)
    os.system('cythonize -i -3 -b atcoder.pyx')

N,M=map(int,input().split())
S=[input() for i in range(N)]
g=mcf_graph_(N*M+2)
s,t=N*M,N*M+1
X=10**9
for i in range(N):
    for j in range(M):
        if S[i][j]=='o':
            g.add_edge(s,i*M+j,1,0)
        if j<M-1 and S[i][j]!='#'!=S[i][j+1]:
            g.add_edge(i*M+j,i*M+j+1,X,0)
        if i<N-1 and S[i][j]!='#'!=S[i+1][j]:
            g.add_edge(i*M+j,(i+1)*M+j,X,0)
        g.add_edge(i*M+j,t,1,X-i-j)
g.flow(s,t)
print(sum([e[3] for e in g.edges() if e[0]!=s and e[1]!=t]))

Measured memory_bytes: 13828