macro_rules! read_line_to_tuple {
    ( $( $t:ty ),* ) => {{
        let mut input = String::new();
        std::io::stdin().read_line(&mut input).unwrap();
        let mut iter = input.split_whitespace();
        ( $( iter.next().unwrap().parse::<$t>().unwrap() ),* )
    }};
}

use std::collections::BinaryHeap;
use std::cmp::Reverse;

#[derive(Clone, Copy)]
struct Edge {
    to: usize, rev: usize, cap: i64, cost: i64,
}

impl Edge {
    fn new(to: usize, rev: usize, cap: i64, cost: i64) -> Edge {
        Edge { to, rev, cap, cost, }
    }
}

struct PrimalDual {
    n: usize,
    g: Vec<Vec<Edge>>,
}

impl PrimalDual {
    fn new(n: usize) -> PrimalDual {
        PrimalDual {
            n,
            g: vec![vec![]; n],
        }
    }

    fn add_edge(&mut self, from: usize, to: usize, cap: i64, cost: i64) {
        let rev = self.g[to].len();
        self.g[from].push(Edge::new(to, rev, cap, cost));
        let rev = self.g[from].len() - 1;
        self.g[to].push(Edge::new(from, rev, 0, -cost));
    }

    fn min_cost_flow(&mut self, s: usize, t: usize, flow_limit: i64) -> i64 {
        let mut res = 0;
        let mut heap = BinaryHeap::new();
        let mut min_cost = vec![0; self.n];
        let mut prev_v = vec![!0; self.n];
        let mut prev_e = vec![!0; self.n];

        let mut flow = 0;
        while flow < flow_limit {
            min_cost.iter_mut().for_each(|c| *c = std::i64::MAX);
            prev_v.iter_mut().for_each(|v| *v = !0);
            prev_e.iter_mut().for_each(|e| *e = !0);
            heap.push((Reverse(0), s));
            min_cost[s] = 0;
            while let Some((Reverse(cost), v)) = heap.pop() {
                for (i, e) in self.g[v].iter().enumerate() {
                    let next_cost = cost + e.cost;
                    if e.cap > 0 && next_cost < min_cost[e.to] {
                        min_cost[e.to] = next_cost;
                        prev_v[e.to] = v;
                        prev_e[e.to] = i;
                        heap.push((Reverse(next_cost), e.to));
                    }
                }
            }

            let mut v = t;
            while v != s {
                self.g[prev_v[v]][prev_e[v]].cap -= 1;
                let rev = self.g[prev_v[v]][prev_e[v]].rev;
                self.g[v][rev].cap += 1;
                res += self.g[prev_v[v]][prev_e[v]].cost;
                v = prev_v[v];
            }

            flow += 1;
        }

        res
    }
}

use std::collections::VecDeque;
use std::cmp::max;

fn main() {
    let (n, m) = read_line_to_tuple!(usize, usize);
    let grid: Vec<Vec<char>> = {
        (0..n)
            .map(|_| read_line_to_tuple!(String).chars().collect())
            .collect()
    };

    let mut pd = PrimalDual::new(n * m + 2);
    let (s, t) = (n * m, n * m + 1);
    let mut k = 0;

    let mut que = VecDeque::with_capacity(max(n, m));
    let mut check = vec![false; n * m];
    for i in 0..n {
        for j in 0..m {
            if grid[i][j] != '#' {
                pd.add_edge(i * m + j, t, 1, 0);
            }
            if grid[i][j] != 'o' { continue; }
            pd.add_edge(s, i * m + j, 1, 0);
            k += 1;
            que.clear();
            check.iter_mut().for_each(|c| *c = false);
            que.push_back(i * m + j);
            check[i * m + j] = true;
            while let Some(v) = que.pop_front() {
                let (x, y) = (v / m, v % m);
                pd.add_edge(i * m + j, x * m + y, 1, -1 * ((x - i) + (y - j)) as i64);
                if x + 1 < n && grid[x + 1][y] != '#' && !check[(x + 1) * m + y] {
                    que.push_back((x + 1) * m + y);
                    check[(x + 1) * m + y] = true;
                }
                if y + 1 < m && grid[x][y + 1] != '#' && !check[x * m + (y + 1)] {
                    que.push_back(x * m + (y + 1));
                    check[x * m + (y + 1)] = true;
                }
            }
        }
    }

    println!("{}", -pd.min_cost_flow(s, t, k));
}

Measured memory_bytes: 8268