#include <algorithm>
#include <chrono>
#include <cmath>
#include <deque>
#include <fstream>
#include <functional>
#include <iostream>
#include <iterator>
#include <limits>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#define forr(i, start, count) for (int i = (start); i < (start) + (count); ++i)
#define set_map_includes(set, elt) (set.find((elt)) != set.end())
#define readint(i)                                                             \\
  int i;                                                                       \\
  cin >> i
#define readll(i)                                                              \\
  ll i;                                                                        \\
  cin >> i
#define readdouble(i)                                                          \\
  double i;                                                                    \\
  cin >> i
#define readstring(s)                                                          \\
  string s;                                                                    \\
  cin >> s

typedef long long ll;

using namespace std;

ll modd = 1000 * 1000 * 1000 + 7;

class UnionFind {
public:
  vector<int> parent;
  vector<int> size;
  //      size[i] = number of nodes in tree rooted at i
  // Note: not necessarily correct if i is not a root node
  set<int> roots;

  UnionFind(int n) : parent(n), size(n) {
    forr(i, 0, n) { roots.insert(i); }
    forr(i, 0, n) {
      parent[i] = i;
      size[i] = 1;
    }
  }

  int Find(int p) {
    int root = p;
    while (root != parent[root]) {
      root = parent[root];
    }
    while (p != root) {
      int newp = parent[p];
      parent[p] = root;
      p = newp;
    }
    return root;
  }

  void Union(int p, int q) {
    int rootP = Find(p);
    int rootQ = Find(q);
    if (rootP == rootQ)
      return;

    if (size[rootP] < size[rootQ]) {
      parent[rootP] = rootQ;
      size[rootQ] += size[rootP];
      roots.erase(rootP);
    } else {
      parent[rootQ] = rootP;
      size[rootP] += size[rootQ];
      roots.erase(rootQ);
    }
  }
};

int main() {

  ios_base::sync_with_stdio(false);

  cout.precision(17);
  mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
  uniform_int_distribution<int> rand_gen(
      0, modd); // rand_gen(rng) gets the rand no

  //    readint(test_cases);
  int test_cases = 1;
  forr(t, 1, test_cases) {
    readint(n);
    map<int, int> c;
    vector<int> dict;
    forr(i, 0, n) {
      readint(a);
      readint(b);
      c[a] = b;
      dict.push_back(a);
    }
    UnionFind uf(n + 1);
    map<int, int> lowest_y;
    for (auto x : c) {
      bool connected = false;
      for (auto y : lowest_y) {
        if (y.first < x.second) {
          connected = true;
          int which = y.second;
          uf.Union(which, x.first);
        }
      }
      if (!connected) {
        lowest_y[x.second] = x.first;
      }
    }

    forr(i, 0, n) { cout << uf.size[uf.Find(dict[i])] << endl; }
  }
  return 0;
}

#include <algorithm>
#include <chrono>
#include <cmath>
#include <deque>
#include <fstream>
#include <functional>
#include <iostream>
#include <iterator>
#include <limits>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#define forr(i, start, count) for (int i = (start); i < (start) + (count); ++i)
#define set_map_includes(set, elt) (set.find((elt)) != set.end())
#define readint(i)                                                             \\
  int i;                                                                       \\
  cin >> i
#define readll(i)                                                              \\
  ll i;                                                                        \\
  cin >> i
#define readdouble(i)                                                          \\
  double i;                                                                    \\
  cin >> i
#define readstring(s)                                                          \\
  string s;                                                                    \\
  cin >> s

typedef long long ll;

using namespace std;

ll modd = 1000 * 1000 * 1000 + 7;

class UnionFind {
public:
  vector<int> parent;
  vector<int> size;
  //      size[i] = number of nodes in tree rooted at i
  // Note: not necessarily correct if i is not a root node
  set<int> roots;

  UnionFind(int n) : parent(n), size(n) {
    forr(i, 0, n) { roots.insert(i); }
    forr(i, 0, n) {
      parent[i] = i;
      size[i] = 1;
    }
  }

  int Find(int p) {
    int root = p;
    while (root != parent[root]) {
      root = parent[root];
    }
    while (p != root) {
      int newp = parent[p];
      parent[p] = root;
      p = newp;
    }
    return root;
  }

  void Union(int p, int q) {
    int rootP = Find(p);
    int rootQ = Find(q);
    if (rootP == rootQ)
      return;

    if (size[rootP] < size[rootQ]) {
      parent[rootP] = rootQ;
      size[rootQ] += size[rootP];
      roots.erase(rootP);
    } else {
      parent[rootQ] = rootP;
      size[rootP] += size[rootQ];
      roots.erase(rootQ);
    }
  }
};

int main() {

  ios_base::sync_with_stdio(false);

  cout.precision(17);
  mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
  uniform_int_distribution<int> rand_gen(
      0, modd); // rand_gen(rng) gets the rand no

  //    readint(test_cases);
  int test_cases = 1;
  forr(t, 1, test_cases) {
    readint(n);
    map<int, int> c;
    vector<int> dict;
    forr(i, 0, n) {
      readint(a);
      readint(b);
      c[a] = b;
      dict.push_back(a);
    }
    UnionFind uf(n + 1);
    map<int, int> lowest_y;
    for (auto x : c) {
      bool connected = false;
      for (auto it = lowest_y.begin(); it != lowest_y.end(); ++it) {
        if (it->first < x.second) {
          connected = true;
          int which = it->second;
          uf.Union(which, x.first);
        } else {
          break;
        }
      }
      if (!connected) {
        lowest_y[x.second] = x.first;
      }
    }

    forr(i, 0, n) { cout << uf.size[uf.Find(dict[i])] << endl; }
  }
  return 0;
}


#include <bits/stdc++.h>
#define min3(a, b, c) min(a, min(b, c))
#define max3(a, b, c) max(a, max(b, c))
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
struct UnionFind {
  vector<int> r;
  int n;
  UnionFind(int a) {
    n = a;
    r.assign(a + 1, -1);
  }
  int root(int x) {
    if (r[x] < 0)
      return x;
    return r[x] = root(r[x]);
  }
  void unite(int a, int b) {
    int x = root(a);
    int y = root(b);
    if (x == y)
      return;
    if (r[x] > r[y])
      swap(x, y);
    r[x] += r[y];
    r[y] = x;
    return;
  }
  int query(int x) { return -r[root(x)]; }
};
int main() {
#ifndef ONLINE_JUDGE
  freopen("input.txt", "r", stdin);
  freopen("output.txt", "w", stdout);
#endif
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);
  int n;
  cin >> n;
  vector<pair<int, int>> pos;
  vector<int> f(n + 1);
  for (int i = 0; i < n; i++) {
    int x, y;
    cin >> x >> y;
    f[y] = i + 1;
    pos.push_back(make_pair(x, y));
  }
  sort(pos.begin(), pos.end());
  UnionFind uf(n);
  set<int, greater<int>> se;
  for (pair<int, int> p : pos) {
    auto it = se.insert(p.second).first;
    int id1 = f[p.second];
    for (++it; it != se.end(); it++) {
      int id2 = f[(*it)];
      uf.unite(id1, id2);
    }
  }
  for (int i = 1; i <= n; i++) {
    cout << uf.query(i) << endl;
  }
}

#include <bits/stdc++.h>
#define min3(a, b, c) min(a, min(b, c))
#define max3(a, b, c) max(a, max(b, c))
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
struct UnionFind {
  vector<int> r;
  int n;
  UnionFind(int a) {
    n = a;
    r.assign(a + 1, -1);
  }
  int root(int x) {
    if (r[x] < 0)
      return x;
    return r[x] = root(r[x]);
  }
  void unite(int a, int b) {
    int x = root(a);
    int y = root(b);
    if (x == y)
      return;
    if (r[x] > r[y])
      swap(x, y);
    r[x] += r[y];
    r[y] = x;
    return;
  }
  int query(int x) { return -r[root(x)]; }
};
int main() {
#ifndef ONLINE_JUDGE
  freopen("input.txt", "r", stdin);
  freopen("output.txt", "w", stdout);
#endif
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);
  int n;
  cin >> n;
  vector<pair<int, int>> pos;
  vector<int> f(n + 1);
  for (int i = 0; i < n; i++) {
    int x, y;
    cin >> x >> y;
    f[y] = i + 1;
    pos.push_back(make_pair(x, y));
  }
  sort(pos.begin(), pos.end());
  UnionFind uf(n);
  set<int, greater<int>> se;
  for (pair<int, int> p : pos) {
    auto it = se.insert(p.second).first;
    int id1 = f[p.second];
    auto i = it;
    i++;
    for (i; i != se.end(); i++) {
      int id2 = f[(*i)];
      uf.unite(id1, id2);
      i = se.erase(it);
      it = i;
    }
  }
  for (int i = 1; i <= n; i++) {
    cout << uf.query(i) << endl;
  }
}


#include <bits/stdc++.h>

using namespace std;

class help {
public:
  int a, b, k;

  help(int x, int y, int z) {
    a = x;
    b = y;
    k = z;
  }
};

bool xsort(help &a, help &b) { return a.a < b.a; }
bool ysort(help &a, help &b) { return a.b < b.b; }

unordered_map<int, int> par;
unordered_map<int, int> r;
int find(int a) {
  if (par.count(a) != 1) {
    par[a] = a;
  }

  if (par[a] == a)
    return a;
  else {
    int pr = find(par[a]);
    par[a] = pr;
    return pr;
  }
}

void merge(int a, int b) {

  int pra = find(a);

  int prb = find(b);

  if (pra != prb) {

    if (r[pra] > r[prb]) {
      par[prb] = pra;
    } else if (r[pra] < r[prb]) {
      par[pra] = prb;
    } else {
      r[pra] = r[pra] + 1;
      par[prb] = pra;
    }
  }
}

int main() {
  int n;
  cin >> n;
  vector<help> arr;
  for (int i = 0; i < n; i++) {
    int a, b;
    cin >> a >> b;
    a--;
    b--;
    arr.push_back(help(a, b, i));
  }
  sort(arr.begin(), arr.end(), xsort);

  set<int> set;
  unordered_map<int, int> tmap;

  for (int i = 0; i < n; i++) {
    int val = -1 * arr[i].b;
    auto it = set.upper_bound(val);
    if (it != set.end()) {
      int in = arr[tmap[*it]].k;
      merge(in, arr[i].k);
    }
    set.insert(val);
    tmap[val] = i;
  }

  set.clear();
  tmap.clear();
  for (int i = n - 1; i >= 0; i--) {
    int val = arr[i].b;
    auto it = set.upper_bound(val);
    if (it != set.end()) {
      int in = arr[tmap[*it]].k;
      merge(in, arr[i].k);
    }
    set.insert(val);
    tmap[val] = i;
  }

  sort(arr.begin(), arr.end(), ysort);

  set.clear();
  tmap.clear();

  for (int i = 0; i < n; i++) {
    int val = -1 * arr[i].a;
    auto it = set.upper_bound(val);
    if (it != set.end()) {
      int in = arr[tmap[*it]].k;
      merge(in, arr[i].k);
    }
    set.insert(val);
    tmap[val] = i;
  }

  set.clear();
  tmap.clear();
  for (int i = n - 1; i >= 0; i--) {
    int val = arr[i].a;
    auto it = set.upper_bound(val);
    if (it != set.end()) {
      int in = arr[tmap[*it]].k;
      merge(in, arr[i].k);
    }
    set.insert(val);
    tmap[val] = i;
  }
  tmap.clear();
  for (int i = 0; i < n; i++) {
    tmap[find(i)]++;
  }
  for (int i = 0; i < n; i++)
    cout << tmap[find(i)] << endl;
}

#include <bits/stdc++.h>

using namespace std;

class help {
public:
  int a, b, k;

  help(int x, int y, int z) {
    a = x;
    b = y;
    k = z;
  }
};

bool xsort(help &a, help &b) { return a.a < b.a; }
bool ysort(help &a, help &b) { return a.b < b.b; }

int p[200000], sz[200000];
int find(int v) { return (v == p[v]) ? v : p[v] = find(p[v]); }
void merge(int a, int b) {
  a = find(a), b = find(b);
  if (a == b)
    return;
  if (sz[a] < sz[b])
    swap(a, b);
  p[b] = a, sz[a] += sz[b];
}

int main() {

  int n;
  cin >> n;

  for (int i = 0; i < n; i++)
    p[i] = i;
  vector<help> arr;
  for (int i = 0; i < n; i++) {
    int a, b;
    cin >> a >> b;
    a--;
    b--;
    arr.push_back(help(a, b, i));
  }
  sort(arr.begin(), arr.end(), xsort);

  set<int> set;
  unordered_map<int, int> tmap;

  for (int i = 0; i < n; i++) {
    int val = -1 * arr[i].b;
    auto it = set.upper_bound(val);
    if (it != set.end()) {
      int in = arr[tmap[*it]].k;
      merge(in, arr[i].k);
    }
    set.insert(val);
    tmap[val] = i;
  }

  set.clear();
  tmap.clear();
  for (int i = n - 1; i >= 0; i--) {
    int val = arr[i].b;
    auto it = set.upper_bound(val);
    if (it != set.end()) {
      int in = arr[tmap[*it]].k;
      merge(in, arr[i].k);
    }
    set.insert(val);
    tmap[val] = i;
  }

  sort(arr.begin(), arr.end(), ysort);

  set.clear();
  tmap.clear();

  for (int i = 0; i < n; i++) {
    int val = -1 * arr[i].a;
    auto it = set.upper_bound(val);
    if (it != set.end()) {
      int in = arr[tmap[*it]].k;
      merge(in, arr[i].k);
    }
    set.insert(val);
    tmap[val] = i;
  }

  set.clear();
  tmap.clear();
  for (int i = n - 1; i >= 0; i--) {
    int val = arr[i].a;
    auto it = set.upper_bound(val);
    if (it != set.end()) {
      int in = arr[tmap[*it]].k;
      merge(in, arr[i].k);
    }
    set.insert(val);
    tmap[val] = i;
  }
  tmap.clear();
  for (int i = 0; i < n; i++) {
    tmap[find(i)]++;
  }
  for (int i = 0; i < n; i++)
    cout << tmap[find(i)] << endl;
}


#include <bits/stdc++.h>

using namespace std;

typedef int ll;
typedef vector<ll> vll;
typedef vector<vll> vvll;
typedef pair<ll, ll> P;
typedef string str;
typedef vector<P> vp;
typedef vector<string> vs;
typedef vector<bool> vb;

const ll mod = 1e9 + 7;
const ll inf = 1e18;

#define rep(i, m, n) for (ll i = m; i < n; i++)
#define repr(i, m, n) for (ll i = m - 1; i >= n; i--)
#define fi first
#define se second
#define chmax(x, y) x = max(x, y)
#define chmin(x, y) x = min(x, y)
#define eb(x) emplace_back(x)
#define pb(x) pop_back(x)
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define sum(x) accumulate(all(x), 0)
#define lb(x, a) lower_bound(all(x), a)
#define ub(x, a) upper_bound(all(x), a)
#define pc(x) __builtin_popcount(x)
#define gll greater<ll>()

struct UnionFind {
  vector<int> par, si;

  UnionFind(int N) : par(N), si(N, 1) { rep(i, 0, N) par[i] = i; }

  int root(int x) {
    if (par[x] == x)
      return x;
    return par[x] = root(par[x]);
  }

  void unite(int x, int y) {
    x = root(x);
    y = root(y);
    if (x == y)
      return;
    si[y] += si[x];
    par[x] = y;
  }

  bool same(int x, int y) { return root(x) == root(y); }
  int size(int x) { return si[root(x)]; }
};

void solve() {
  ll n;
  cin >> n;

  vp x(n);
  map<P, ll> mp;
  rep(i, 0, n) {
    cin >> x[i].fi >> x[i].se;
    mp[x[i]] = i;
  }
  sort(all(x));
  UnionFind u(n);

  set<P> l, r;
  rep(i, 0, n) r.insert(P(x[i].se, mp[x[i]]));

  rep(i, 0, n) {
    P p = P(x[i].se, mp[x[i]]);

    auto z = r.upper_bound(p);
    while (z != r.end()) {
      u.unite(p.se, z->se);
      z++;
    }

    r.erase(p);
  }

  rep(i, 0, n) cout << u.size(i) << endl;
}
int main() {
  cin.tie(nullptr);
  ios::sync_with_stdio(false);
  solve();
}

#include <bits/stdc++.h>

using namespace std;

typedef int ll;
typedef vector<ll> vll;
typedef vector<vll> vvll;
typedef pair<ll, ll> P;
typedef string str;
typedef vector<P> vp;
typedef vector<string> vs;
typedef vector<bool> vb;

const ll mod = 1e9 + 7;
const ll inf = 1e18;

#define rep(i, m, n) for (ll i = m; i < n; i++)
#define repr(i, m, n) for (ll i = m - 1; i >= n; i--)
#define fi first
#define se second
#define chmax(x, y) x = max(x, y)
#define chmin(x, y) x = min(x, y)
#define eb(x) emplace_back(x)
#define pb(x) pop_back(x)
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define sum(x) accumulate(all(x), 0)
#define lb(x, a) lower_bound(all(x), a)
#define ub(x, a) upper_bound(all(x), a)
#define pc(x) __builtin_popcount(x)
#define gll greater<ll>()

struct UnionFind {
  vector<int> par, si;

  UnionFind(int N) : par(N), si(N, 1) { rep(i, 0, N) par[i] = i; }

  int root(int x) {
    if (par[x] == x)
      return x;
    return par[x] = root(par[x]);
  }

  void unite(int x, int y) {
    x = root(x);
    y = root(y);
    if (x == y)
      return;
    si[y] += si[x];
    par[x] = y;
  }

  bool same(int x, int y) { return root(x) == root(y); }
  int size(int x) { return si[root(x)]; }
};

void solve() {
  ll n;
  cin >> n;
  vll id(n), y(n);
  rep(i, 0, n) {
    ll u, v;
    cin >> u >> v;
    --u;
    --v;
    id[u] = i;
    y[u] = v;
  }

  UnionFind u(n);
  set<P> s;
  repr(i, n, 0) {
    P p = P(y[i], id[i]);
    auto it = s.lower_bound(p);
    while (it != s.end()) {
      u.unite(it->se, id[i]);
      p = *it;
      it = s.erase(it);
    }
    s.insert(p);
  }

  rep(i, 0, n) cout << u.size(i) << endl;
}
int main() {
  cin.tie(nullptr);
  ios::sync_with_stdio(false);
  solve();
}


#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
using namespace std;

typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef long long ll;

template <typename _T> inline void read(_T &f) {
  f = 0;
  _T fu = 1;
  char c = getchar();
  while (c < '0' || c > '9') {
    if (c == '-') {
      fu = -1;
    }
    c = getchar();
  }
  while (c >= '0' && c <= '9') {
    f = (f << 3) + (f << 1) + (c & 15);
    c = getchar();
  }
  f *= fu;
}

template <typename T> void print(T x) {
  if (x < 0)
    putchar('-'), x = -x;
  if (x < 10)
    putchar(x + 48);
  else
    print(x / 10), putchar(x % 10 + 48);
}

template <typename T> void print(T x, char t) {
  print(x);
  putchar(t);
}

const int N = 1e5 + 5;

int a[N], b[N], ida[N], idb[N], vis[N], ans[N];
int n, cnt;

bool cmpa(int x, int y) { return a[x] < a[y]; }
bool cmpb(int x, int y) { return b[x] < b[y]; }

int main() {
  read(n);
  for (int i = 1; i <= n; i++)
    read(a[i]), read(b[i]), ida[i] = idb[i] = i;
  sort(ida + 1, ida + n + 1, cmpa);
  sort(idb + 1, idb + n + 1, cmpb);
  int las = 0;
  for (int i = 1; i <= n; i++) {
    if (!vis[ida[i]])
      vis[ida[i]] = 1, ++cnt;
    if (!vis[idb[n - i + 1]])
      vis[idb[n - i + 1]] = 1, ++cnt;
    if (cnt == i) {
      for (int j = las + 1; j <= i; j++)
        ans[ida[j]] = i - las;
      las = i;
    }
  }
  for (int i = 1; i <= n; i++)
    print(ans[i], '\\n');
  return 0;
}

#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
using namespace std;

typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef long long ll;

template <typename _T> inline void read(_T &f) {
  f = 0;
  _T fu = 1;
  char c = getchar();
  while (c < '0' || c > '9') {
    if (c == '-') {
      fu = -1;
    }
    c = getchar();
  }
  while (c >= '0' && c <= '9') {
    f = (f << 3) + (f << 1) + (c & 15);
    c = getchar();
  }
  f *= fu;
}

template <typename T> void print(T x) {
  if (x < 0)
    putchar('-'), x = -x;
  if (x < 10)
    putchar(x + 48);
  else
    print(x / 10), putchar(x % 10 + 48);
}

template <typename T> void print(T x, char t) {
  print(x);
  putchar(t);
}

const int N = 2e5 + 5;

int a[N], b[N], ida[N], idb[N], vis[N], ans[N];
int n, cnt;

bool cmpa(int x, int y) { return a[x] < a[y]; }
bool cmpb(int x, int y) { return b[x] < b[y]; }

int main() {
  read(n);
  for (int i = 1; i <= n; i++)
    read(a[i]), read(b[i]), ida[i] = idb[i] = i;
  sort(ida + 1, ida + n + 1, cmpa);
  sort(idb + 1, idb + n + 1, cmpb);
  int las = 0;
  for (int i = 1; i <= n; i++) {
    if (!vis[ida[i]])
      vis[ida[i]] = 1, ++cnt;
    if (!vis[idb[n - i + 1]])
      vis[idb[n - i + 1]] = 1, ++cnt;
    if (cnt == i) {
      for (int j = las + 1; j <= i; j++)
        ans[ida[j]] = i - las;
      las = i;
    }
  }
  for (int i = 1; i <= n; i++)
    print(ans[i], '\\n');
  return 0;
}


#pragma GCC optimize(3)
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define mkp make_pair
#define rint register int
#define INF ((1 << 30) - 1)
#define FI(n) FastIO::read(n)
#define FO(n) FastIO::write(n)
#define Pair pair<int, int>
#define mst(a, b) memset(a, b, sizeof(a))
#define foR(i, k, j) for (rint i = (k); i >= (j); i--)
#define For(i, k, j) for (rint i = (k); i <= (j); i++)
#define Foe(i, u) for (rint i = lst[u], v = e[i].v; i; i = e[i].nxt, v = e[i].v)
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define file(s)                                                                \\
  freopen(s ".in", "r", stdin);                                                \\
  freopen(s ".out", "w", stdout)
// #define int long long
const int P = 1000000007; //
using namespace std;
inline void ckmax(int &a, int b) { a = max(a, b); }
inline void ckmin(int &a, int b) { a = min(a, b); }
inline void mulmod(int &a, int b) { a = 1ll * a * b % P; }
inline void addmod(int &a, int b) {
  int t = a + b;
  a = (t >= P ? t - P : t);
}
inline int ksm(int a, int b) {
  int ans = 1;
  for (; b; b >>= 1) {
    if (b & 1)
      ans = 1ll * ans * a % P;
    a = 1ll * a * a % P;
  }
  return ans;
}
inline int inv(int a) { return ksm(a, P - 2); }

inline void printarray(int *a, int n) {
  For(i, 1, n) fprintf(stderr, "%d ", a[i]);
  fprintf(stderr, "\\n");
}
namespace FastIO {
const int SIZE = 1 << 16;
char buf[SIZE], obuf[SIZE], str[64];
int bi = SIZE, bn = SIZE, opt;
int read(char *s) {
  while (bn) {
    for (; bi < bn && buf[bi] <= ' '; bi++)
      ;
    if (bi < bn)
      break;
    bn = fread(buf, 1, SIZE, stdin), bi = 0;
  }
  int sn = 0;
  while (bn) {
    for (; bi < bn && buf[bi] > ' '; bi++)
      s[sn++] = buf[bi];
    if (bi < bn)
      break;
    bn = fread(buf, 1, SIZE, stdin), bi = 0;
  }
  s[sn] = 0;
  return sn;
}
bool read(int &x) {
  if (x)
    x = 0;
  int bf = 0, n = read(str);
  if (!n)
    return 0;
  int i = 0;
  if (str[i] == '-')
    bf = 1, i = 1;
  for (x = 0; i < n; i++)
    x = x * 10 + str[i] - '0';
  if (bf)
    x = -x;
  return 1;
}
void write(int x) {
  if (!x)
    obuf[opt++] = '0';
  else {
    if (x < 0)
      obuf[opt++] = '-', x = -x;
    int sn = 0;
    while (x)
      str[sn++] = x % 10 + '0', x /= 10;
    for (int i = sn - 1; i >= 0; i--)
      obuf[opt++] = str[i];
  }
  if (opt >= (SIZE >> 1)) {
    fwrite(obuf, 1, opt, stdout);
    opt = 0;
  }
}
void write(char x) {
  obuf[opt++] = x;
  if (opt >= (SIZE >> 1)) {
    fwrite(obuf, 1, opt, stdout);
    opt = 0;
  }
}
void Fflush() {
  if (opt)
    fwrite(obuf, 1, opt, stdout);
  opt = 0;
}
}; // namespace FastIO
const int MAXN = 2e5 + 5;
struct Node {
  int x, y, id;
  bool operator<(const Node &b) const { return x < b.x; }
} a[MAXN];
int n, f[MAXN], siz[MAXN];
inline int find(int x) {
  while (x != f[x])
    x = f[x] = f[f[x]];
  return x;
}
inline int merge(int x, int y) {
  x = find(x), y = find(y);
  if (x == y)
    return 0;
  siz[x] += siz[y], f[y] = x;
}
int s[MAXN], top, ans[MAXN];
signed main() {
#ifndef ONLINE_JUDGE
  file("pro");
#endif
  FI(n);
  For(i, 1, n) FI(a[i].x), FI(a[i].y), a[i].id = i, f[i] = i, siz[i] = 1;
  sort(a + 1, a + 1 + n);
  For(i, 1, n) {
    while (top && a[s[top]].y < a[i].y) {
      merge(i, s[top]);
      top--;
    }
    s[++top] = i;
  }
  For(i, 1, n) { ans[a[i].id] = siz[find(i)]; }
  For(i, 1, n) {
    FO(ans[i]);
    FO('\\n');
  }
  return FastIO::Fflush(), 0;
}
/*
Think twice :
mod ?
INF ?
n = 1 ?
long long ?
Fastio::Fflush() ?

*/

#pragma GCC optimize(3)
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define mkp make_pair
#define rint register int
#define INF ((1 << 30) - 1)
#define FI(n) FastIO::read(n)
#define FO(n) FastIO::write(n)
#define Pair pair<int, int>
#define mst(a, b) memset(a, b, sizeof(a))
#define foR(i, k, j) for (rint i = (k); i >= (j); i--)
#define For(i, k, j) for (rint i = (k); i <= (j); i++)
#define Foe(i, u) for (rint i = lst[u], v = e[i].v; i; i = e[i].nxt, v = e[i].v)
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define file(s)                                                                \\
  freopen(s ".in", "r", stdin);                                                \\
  freopen(s ".out", "w", stdout)
// #define int long long
const int P = 1000000007; //
using namespace std;
inline void ckmax(int &a, int b) { a = max(a, b); }
inline void ckmin(int &a, int b) { a = min(a, b); }
inline void mulmod(int &a, int b) { a = 1ll * a * b % P; }
inline void addmod(int &a, int b) {
  int t = a + b;
  a = (t >= P ? t - P : t);
}
inline int ksm(int a, int b) {
  int ans = 1;
  for (; b; b >>= 1) {
    if (b & 1)
      ans = 1ll * ans * a % P;
    a = 1ll * a * a % P;
  }
  return ans;
}
inline int inv(int a) { return ksm(a, P - 2); }

inline void printarray(int *a, int n) {
  For(i, 1, n) fprintf(stderr, "%d ", a[i]);
  fprintf(stderr, "\\n");
}
namespace FastIO {
const int SIZE = 1 << 16;
char buf[SIZE], obuf[SIZE], str[64];
int bi = SIZE, bn = SIZE, opt;
int read(char *s) {
  while (bn) {
    for (; bi < bn && buf[bi] <= ' '; bi++)
      ;
    if (bi < bn)
      break;
    bn = fread(buf, 1, SIZE, stdin), bi = 0;
  }
  int sn = 0;
  while (bn) {
    for (; bi < bn && buf[bi] > ' '; bi++)
      s[sn++] = buf[bi];
    if (bi < bn)
      break;
    bn = fread(buf, 1, SIZE, stdin), bi = 0;
  }
  s[sn] = 0;
  return sn;
}
bool read(int &x) {
  if (x)
    x = 0;
  int bf = 0, n = read(str);
  if (!n)
    return 0;
  int i = 0;
  if (str[i] == '-')
    bf = 1, i = 1;
  for (x = 0; i < n; i++)
    x = x * 10 + str[i] - '0';
  if (bf)
    x = -x;
  return 1;
}
void write(int x) {
  if (!x)
    obuf[opt++] = '0';
  else {
    if (x < 0)
      obuf[opt++] = '-', x = -x;
    int sn = 0;
    while (x)
      str[sn++] = x % 10 + '0', x /= 10;
    for (int i = sn - 1; i >= 0; i--)
      obuf[opt++] = str[i];
  }
  if (opt >= (SIZE >> 1)) {
    fwrite(obuf, 1, opt, stdout);
    opt = 0;
  }
}
void write(char x) {
  obuf[opt++] = x;
  if (opt >= (SIZE >> 1)) {
    fwrite(obuf, 1, opt, stdout);
    opt = 0;
  }
}
void Fflush() {
  if (opt)
    fwrite(obuf, 1, opt, stdout);
  opt = 0;
}
}; // namespace FastIO
const int MAXN = 2e5 + 5;
struct Node {
  int x, y, id;
  bool operator<(const Node &b) const { return x < b.x; }
} a[MAXN];
int n, f[MAXN], siz[MAXN];
inline int find(int x) {
  while (x != f[x])
    x = f[x] = f[f[x]];
  return x;
}
inline void merge(int x, int y) {
  x = find(x), y = find(y);
  if (x != y)
    siz[x] += siz[y], f[y] = x;
}
int s[MAXN], top, v[MAXN], ans[MAXN];
signed main() {
#ifndef ONLINE_JUDGE
  file("pro");
#endif
  FI(n);
  For(i, 1, n) FI(a[i].x), FI(a[i].y), a[i].id = i, f[i] = i, siz[i] = 1;
  sort(a + 1, a + 1 + n);
  For(i, 1, n) {
    int y = a[i].y;
    while (top && v[top] < a[i].y) {
      merge(i, s[top]);
      ckmin(y, v[top]);
      top--;
    }
    s[++top] = i, v[top] = y;
  }
  For(i, 1, n) ans[a[i].id] = siz[find(i)];
  For(i, 1, n) FO(ans[i]), FO('\\n');

  return FastIO::Fflush(), 0;
}
/*
Think twice :
mod ?
INF ?
n = 1 ?
long long ?
Fastio::Fflush() ?

*/


#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int N = 3e5 + 10;
const int mod = 998244353;
int n;
int k, m;
int a[N];
int b[N];

map<int, int> mp;
struct pp {
  int x;
  int y;
  int index;
} p[N];

int cmp(pp a, pp b) { return a.x < b.x; }
int num[N];
int ans[N];
int parent[N];
set<int> s;
void Init() {
  for (int i = 1; i <= n + 10; i++) {
    parent[i] = i;
    num[i] = 1;
  }
  return;
}

int get_boss(int v) {
  if (parent[v] == v)
    return v;
  else {
    // 路径压缩(使搜索祖宗的过程中，路径中搜索的结点的父亲的变为祖宗)
    parent[v] = get_boss(parent[v]);
    return parent[v];
  }
}

void Merge(int v, int u) // u合并到v
{
  int t1 = get_boss(v);
  int t2 = get_boss(u);
  if (t1 == t2)
    return;
  if (num[t1] < num[t2])
    swap(t1, t2);
  parent[t2] = t1;
  num[t1] += num[t2];

  return;
}

int main() {
  cin >> n;
  Init();
  for (int i = 1; i <= n; i++) {
    cin >> p[i].x >> p[i].y;
    p[i].index = i;
  }

  sort(p + 1, p + 1 + n, cmp);
  for (int i = 1; i <= n; i++) {
    mp[p[i].y] = i;
  }

  for (int i = 1; i <= n; i++) {
    auto it1 = s.lower_bound(p[i].y);
    int stop = *it1;
    auto it2 = s.begin();
    int start = *it2;

    if (it1 != it2) {
      Merge(mp[start], mp[p[i].y]);
      it2++;
      for (; *it2 != stop;) {
        Merge(mp[start], mp[*it2]);
        it2 = s.erase(it2);
      }
    } else {
      s.insert(p[i].y);
    }
  }

  for (int i = 1; i <= n; i++) {
    ans[p[i].index] = num[get_boss(mp[p[i].y])];
  }
  for (int i = 1; i <= n; i++)
    cout << ans[i] << endl;
}

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int N = 3e5 + 10;
const int mod = 998244353;
int n;
int k, m;
int a[N];
int b[N];

map<int, int> mp;
struct pp {
  int x;
  int y;
  int index;
} p[N];

int cmp(pp a, pp b) { return a.x < b.x; }
int num[N];
int ans[N];
int parent[N];
set<int> s;
void Init() {
  for (int i = 1; i <= n + 10; i++) {
    parent[i] = i;
    num[i] = 1;
  }
  return;
}

int get_boss(int v) {
  if (parent[v] == v)
    return v;
  else {
    // 路径压缩(使搜索祖宗的过程中，路径中搜索的结点的父亲的变为祖宗)
    parent[v] = get_boss(parent[v]);
    return parent[v];
  }
}

void Merge(int v, int u) // u合并到v
{
  int t1 = get_boss(v);
  int t2 = get_boss(u);
  if (t1 == t2)
    return;
  if (num[t1] < num[t2])
    swap(t1, t2);
  parent[t2] = t1;
  num[t1] += num[t2];

  return;
}

int main() {
  cin >> n;
  Init();
  for (int i = 1; i <= n; i++) {
    cin >> p[i].x >> p[i].y;
    p[i].index = i;
  }

  sort(p + 1, p + 1 + n, cmp);
  for (int i = 1; i <= n; i++) {
    mp[p[i].y] = i;
  }

  for (int i = 1; i <= n; i++) {
    auto it1 = s.lower_bound(p[i].y);
    // int stop=*it1;
    auto it2 = s.begin();
    int start = *it2;

    if (it1 != it2) {
      Merge(mp[start], mp[p[i].y]);
      it2++;
      for (; it2 != it1;) {
        Merge(mp[start], mp[*it2]);
        it2 = s.erase(it2);
      }
    } else {
      s.insert(p[i].y);
    }
  }

  for (int i = 1; i <= n; i++) {
    ans[p[i].index] = num[get_boss(mp[p[i].y])];
  }
  for (int i = 1; i <= n; i++)
    cout << ans[i] << endl;
}


#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int N = 3e5 + 10;
const int mod = 998244353;
int n;
int k, m;
int a[N];
int b[N];

map<int, int> mp;
struct pp {
  int x;
  int y;
  int index;
} p[N];

int cmp(pp a, pp b) { return a.x < b.x; }
int num[N];
int ans[N];
int parent[N];
set<int> s;
void Init() {
  for (int i = 1; i <= n + 10; i++) {
    parent[i] = i;
    num[i] = 1;
  }
  return;
}

int get_boss(int v) {
  if (parent[v] == v)
    return v;
  else {
    // 路径压缩(使搜索祖宗的过程中，路径中搜索的结点的父亲的变为祖宗)
    parent[v] = get_boss(parent[v]);
    return parent[v];
  }
}

void Merge(int v, int u) // u合并到v
{
  int t1 = get_boss(v);
  int t2 = get_boss(u);
  parent[t2] = t1;
  num[t1] += num[t2];
  return;
}

int main() {
  cin >> n;
  Init();
  for (int i = 1; i <= n; i++) {
    cin >> p[i].x >> p[i].y;
    p[i].index = i;
  }

  sort(p + 1, p + 1 + n, cmp);
  for (int i = 1; i <= n; i++) {
    mp[p[i].y] = i;
  }

  for (int i = 1; i <= n; i++) {
    auto it1 = s.lower_bound(p[i].y);
    int stop = *it1;
    auto it2 = s.begin();
    int start = *it2;

    if (it1 != it2) {
      Merge(mp[start], mp[p[i].y]);
      it2++;
      for (; *it2 != stop;) {
        Merge(mp[start], mp[*it2]);
        it2 = s.erase(it2);
      }
    } else {
      s.insert(p[i].y);
    }
  }

  for (int i = 1; i <= n; i++) {
    ans[p[i].index] = num[get_boss(mp[p[i].y])];
  }
  for (int i = 1; i <= n; i++)
    cout << ans[i] << endl;
}

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int N = 3e5 + 10;
const int mod = 998244353;
int n;
int k, m;
int a[N];
int b[N];

map<int, int> mp;
struct pp {
  int x;
  int y;
  int index;
} p[N];

int cmp(pp a, pp b) { return a.x < b.x; }
int num[N];
int ans[N];
int parent[N];
stack<int> s;
void Init() {
  for (int i = 1; i <= n + 10; i++) {
    parent[i] = i;
    num[i] = 1;
  }
  return;
}

int get_boss(int v) {
  if (parent[v] == v)
    return v;
  else {
    // 路径压缩(使搜索祖宗的过程中，路径中搜索的结点的父亲的变为祖宗)
    parent[v] = get_boss(parent[v]);
    return parent[v];
  }
}

void Merge(int v, int u) // u合并到v
{
  int t1 = get_boss(v);
  int t2 = get_boss(u);
  parent[t2] = t1;
  num[t1] += num[t2];
  return;
}

int main() {
  cin >> n;
  Init();
  for (int i = 1; i <= n; i++) {
    cin >> p[i].x >> p[i].y;
    p[i].index = i;
  }

  sort(p + 1, p + 1 + n, cmp);
  for (int i = 1; i <= n; i++) {
    mp[p[i].y] = i;
  }

  for (int i = 1; i <= n; i++) {
    int mn = p[i].y;
    while (!s.empty() && s.top() < p[i].y) {
      Merge(mp[s.top()], mp[p[i].y]);
      mn = min(mn, s.top());
      s.pop();
    }
    p[i].y = mn;
    s.push(mn);
  }

  for (int i = 1; i <= n; i++) {
    ans[p[i].index] = num[get_boss(mp[p[i].y])];
  }
  for (int i = 1; i <= n; i++)
    cout << ans[i] << endl;
}


#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < n; i++)
#define repl(i, l, r) for (ll i = (l); i < (r); i++)
#define per(i, n) for (ll i = n - 1; i >= 0; i--)
#define perl(i, r, l) for (ll i = r - 1; i >= l; i--)
#define fi first
#define se second
#define pb push_back
#define ins insert
#define pqueue(x) priority_queue<x, vector<x>, greater<x>>
#define all(x) (x).begin(), (x).end()
#define CST(x) cout << fixed << setprecision(x)
#define rev(x) reverse(x);
using ll = long long;
using vl = vector<ll>;
using vvl = vector<vector<ll>>;
using pl = pair<ll, ll>;
using vpl = vector<pl>;
using vvpl = vector<vpl>;
const ll MOD = 1000000007;
const ll MOD9 = 998244353;
const int inf = 1e9 + 10;
const ll INF = 4e18;
const ll dy[9] = {1, 0, -1, 0, 1, 1, -1, -1, 0};
const ll dx[9] = {0, -1, 0, 1, 1, -1, 1, -1, 0};
template <class T> inline bool chmin(T &a, T b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
template <class T> inline bool chmax(T &a, T b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}

template <class T> class SegTree {
  int n;                       // 葉の数
  vector<T> data;              // データを格納するvector
  T def;                       // 初期値かつ単位元
  function<T(T, T)> operation; // 区間クエリで使う処理
  function<T(T, T)> update;    // 点更新で使う処理

  T find(int a, int b) {
    T val_left = def, val_right = def;
    for (a += (n - 1), b += (n - 1); a < b; a >>= 1, b >>= 1) {
      if ((a & 1) == 0) {
        val_left = operation(val_left, data[a]);
      }
      if ((b & 1) == 0) {
        val_right = operation(data[--b], val_right);
      }
    }
    return operation(val_left, val_right);
  }

public:
  // _n:必要サイズ, _def:初期値かつ単位元, _operation:クエリ関数,
  // _update:更新関数
  SegTree(
      size_t _n, T _def, function<T(T, T)> _operation,
      function<T(T, T)> _update = [](T a, T b) { return b; })
      : def(_def), operation(_operation), update(_update) {
    n = 1;
    while (n < _n) {
      n *= 2;
    }
    data = vector<T>(2 * n - 1, def);
  }
  void set(int i, T x) { data[i + n - 1] = x; }
  void build() {
    for (int k = n - 2; k >= 0; k--)
      data[k] = operation(data[2 * k + 1], data[2 * k + 2]);
  }
  // 場所i(0-indexed)の値をxで更新
  void change(int i, T x) {
    i += n - 1;
    data[i] = update(data[i], x);
    while (i > 0) {
      i = (i - 1) / 2;
      data[i] = operation(data[i * 2 + 1], data[i * 2 + 2]);
    }
  }

  // [a, b)の区間クエリを実行
  T query(int a, int b) {
    // return _query(a, b, 0, 0, n);
    return find(a, b);
  }

  // 添字でアクセス
  T operator[](int i) { return data[i + n - 1]; }
};

struct UnionFind {
  vector<int> par;

  UnionFind(int n) : par(n, -1) {}

  int root(int x) {
    if (par[x] < 0)
      return x;
    else
      return par[x] = root(par[x]);
  }

  bool same(int x, int y) { return root(x) == root(y); }

  bool merge(int x, int y) {
    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    if (par[x] > par[y])
      swap(x, y);
    par[x] += par[y];
    par[y] = x;
    return true;
  }

  int size(int x) { return -par[root(x)]; }
};

int main() {
  ll n;
  cin >> n;
  vpl z(n);
  vl ex(n);
  rep(i, n) {
    ll a, b;
    cin >> a >> b;
    a--;
    b--;
    z[i] = {a, b};
    ex[i] = a;
  }
  sort(all(z));
  UnionFind uf(n);
  set<pl> valid;
  rep(i, n) {
    set<pl> con;
    for (auto p : valid) {
      if (p.fi < z[i].se) {
        uf.merge(p.se, i);
        con.ins(p);
      }
    }
    valid.ins({z[i].se, i});
    con.ins({z[i].se, i});
    con.erase(*con.begin());
    for (auto p : con) {
      valid.erase(p);
    }
  }
  rep(i, n) { cout << uf.size(ex[i]) << endl; }
}

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < n; i++)
#define repl(i, l, r) for (ll i = (l); i < (r); i++)
#define per(i, n) for (ll i = n - 1; i >= 0; i--)
#define perl(i, r, l) for (ll i = r - 1; i >= l; i--)
#define fi first
#define se second
#define pb push_back
#define ins insert
#define pqueue(x) priority_queue<x, vector<x>, greater<x>>
#define all(x) (x).begin(), (x).end()
#define CST(x) cout << fixed << setprecision(x)
#define rev(x) reverse(x);
using ll = long long;
using vl = vector<ll>;
using vvl = vector<vector<ll>>;
using pl = pair<ll, ll>;
using vpl = vector<pl>;
using vvpl = vector<vpl>;
const ll MOD = 1000000007;
const ll MOD9 = 998244353;
const int inf = 1e9 + 10;
const ll INF = 4e18;
const ll dy[9] = {1, 0, -1, 0, 1, 1, -1, -1, 0};
const ll dx[9] = {0, -1, 0, 1, 1, -1, 1, -1, 0};
template <class T> inline bool chmin(T &a, T b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
template <class T> inline bool chmax(T &a, T b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}

template <class T> class SegTree {
  int n;                       // 葉の数
  vector<T> data;              // データを格納するvector
  T def;                       // 初期値かつ単位元
  function<T(T, T)> operation; // 区間クエリで使う処理
  function<T(T, T)> update;    // 点更新で使う処理

  T find(int a, int b) {
    T val_left = def, val_right = def;
    for (a += (n - 1), b += (n - 1); a < b; a >>= 1, b >>= 1) {
      if ((a & 1) == 0) {
        val_left = operation(val_left, data[a]);
      }
      if ((b & 1) == 0) {
        val_right = operation(data[--b], val_right);
      }
    }
    return operation(val_left, val_right);
  }

public:
  // _n:必要サイズ, _def:初期値かつ単位元, _operation:クエリ関数,
  // _update:更新関数
  SegTree(
      size_t _n, T _def, function<T(T, T)> _operation,
      function<T(T, T)> _update = [](T a, T b) { return b; })
      : def(_def), operation(_operation), update(_update) {
    n = 1;
    while (n < _n) {
      n *= 2;
    }
    data = vector<T>(2 * n - 1, def);
  }
  void set(int i, T x) { data[i + n - 1] = x; }
  void build() {
    for (int k = n - 2; k >= 0; k--)
      data[k] = operation(data[2 * k + 1], data[2 * k + 2]);
  }
  // 場所i(0-indexed)の値をxで更新
  void change(int i, T x) {
    i += n - 1;
    data[i] = update(data[i], x);
    while (i > 0) {
      i = (i - 1) / 2;
      data[i] = operation(data[i * 2 + 1], data[i * 2 + 2]);
    }
  }

  // [a, b)の区間クエリを実行
  T query(int a, int b) {
    // return _query(a, b, 0, 0, n);
    return find(a, b);
  }

  // 添字でアクセス
  T operator[](int i) { return data[i + n - 1]; }
};

struct UnionFind {
  vector<int> par;

  UnionFind(int n) : par(n, -1) {}

  int root(int x) {
    if (par[x] < 0)
      return x;
    else
      return par[x] = root(par[x]);
  }

  bool same(int x, int y) { return root(x) == root(y); }

  bool merge(int x, int y) {
    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    if (par[x] > par[y])
      swap(x, y);
    par[x] += par[y];
    par[y] = x;
    return true;
  }

  int size(int x) { return -par[root(x)]; }
};

int main() {
  ll n;
  cin >> n;
  vpl z(n);
  vl ex(n);
  rep(i, n) {
    ll a, b;
    cin >> a >> b;
    a--;
    b--;
    z[i] = {a, b};
    ex[i] = a;
  }
  sort(all(z));
  UnionFind uf(n);
  set<pl> valid;
  rep(i, n) {
    set<pl> con;
    for (auto p : valid) {
      if (p.fi < z[i].se) {
        uf.merge(p.se, i);
        con.ins(p);
      } else
        break;
    }
    valid.ins({z[i].se, i});
    con.ins({z[i].se, i});
    con.erase(*con.begin());
    for (auto p : con) {
      valid.erase(p);
    }
  }
  rep(i, n) { cout << uf.size(ex[i]) << endl; }
}


#include <bits/stdc++.h>
// #include <atcoder/all>
// using namespace atcoder;
#define INF 2000000000000000000
#define ll long long
#define pll pair<ll, ll>
using namespace std;

// ===========Disjoint Set Union================
struct dsu {
public:
  dsu() : _n(0) {}
  dsu(int n) : _n(n), parent_or_size(n, -1) {}

  int merge(int a, int b) {
    assert(0 <= a && a < _n);
    assert(0 <= b && b < _n);
    int x = leader(a), y = leader(b);
    if (x == y)
      return x;
    if (-parent_or_size[x] < -parent_or_size[y])
      std::swap(x, y);
    parent_or_size[x] += parent_or_size[y];
    parent_or_size[y] = x;
    return x;
  }

  bool same(int a, int b) {
    assert(0 <= a && a < _n);
    assert(0 <= b && b < _n);
    return leader(a) == leader(b);
  }

  int leader(int a) {
    assert(0 <= a && a < _n);
    if (parent_or_size[a] < 0)
      return a;
    return parent_or_size[a] = leader(parent_or_size[a]);
  }

  int size(int a) {
    assert(0 <= a && a < _n);
    return -parent_or_size[leader(a)];
  }

  std::vector<std::vector<int>> groups() {
    std::vector<int> leader_buf(_n), group_size(_n);
    for (int i = 0; i < _n; i++) {
      leader_buf[i] = leader(i);
      group_size[leader_buf[i]]++;
    }
    std::vector<std::vector<int>> result(_n);
    for (int i = 0; i < _n; i++) {
      result[i].reserve(group_size[i]);
    }
    for (int i = 0; i < _n; i++) {
      result[leader_buf[i]].push_back(i);
    }
    result.erase(
        std::remove_if(result.begin(), result.end(),
                       [&](const std::vector<int> &v) { return v.empty(); }),
        result.end());
    return result;
  }

private:
  int _n;
  // root node: -1 * component size
  // otherwise: parent
  std::vector<int> parent_or_size;
};
// =======================================

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  ll N;
  cin >> N;
  dsu d(N);
  vector<vector<ll>> P(N);
  set<ll> y_remain;
  vector<ll> X(N), Y(N);
  vector<ll> y_to_index(N + 1);
  for (ll i = 0; i < N; ++i) {
    cin >> X.at(i) >> Y.at(i);
    P.at(i) = {X.at(i), Y.at(i), i};
    y_to_index.at(Y.at(i)) = i;
    y_remain.insert(Y.at(i));
  }
  sort(P.begin(), P.end());
  for (ll i = 0; i < N; ++i) {
    vector<ll> now = P.at(i);
    ll x = now.at(0), y = now.at(1), index = now.at(2);
    auto itr1 = y_remain.lower_bound(y);
    if (itr1 == y_remain.end() || *itr1 != y) {
      continue;
    }
    y_remain.erase(y);
    auto itr = y_remain.lower_bound(y);
    while (itr != y_remain.end()) {
      ll temp_y = *itr;
      d.merge(y_to_index.at(y), y_to_index.at(temp_y));
      itr++;
    }
  }
  for (ll i = 0; i < N; ++i) {
    cout << d.size(i) << "\\n";
  }
}

#include <bits/stdc++.h>
// #include <atcoder/all>
// using namespace atcoder;
#define INF 2000000000000000000
#define ll long long
#define pll pair<ll, ll>
using namespace std;

// ===========Disjoint Set Union================
struct dsu {
public:
  dsu() : _n(0) {}
  dsu(int n) : _n(n), parent_or_size(n, -1) {}

  int merge(int a, int b) {
    assert(0 <= a && a < _n);
    assert(0 <= b && b < _n);
    int x = leader(a), y = leader(b);
    if (x == y)
      return x;
    if (-parent_or_size[x] < -parent_or_size[y])
      std::swap(x, y);
    parent_or_size[x] += parent_or_size[y];
    parent_or_size[y] = x;
    return x;
  }

  bool same(int a, int b) {
    assert(0 <= a && a < _n);
    assert(0 <= b && b < _n);
    return leader(a) == leader(b);
  }

  int leader(int a) {
    assert(0 <= a && a < _n);
    if (parent_or_size[a] < 0)
      return a;
    return parent_or_size[a] = leader(parent_or_size[a]);
  }

  int size(int a) {
    assert(0 <= a && a < _n);
    return -parent_or_size[leader(a)];
  }

  std::vector<std::vector<int>> groups() {
    std::vector<int> leader_buf(_n), group_size(_n);
    for (int i = 0; i < _n; i++) {
      leader_buf[i] = leader(i);
      group_size[leader_buf[i]]++;
    }
    std::vector<std::vector<int>> result(_n);
    for (int i = 0; i < _n; i++) {
      result[i].reserve(group_size[i]);
    }
    for (int i = 0; i < _n; i++) {
      result[leader_buf[i]].push_back(i);
    }
    result.erase(
        std::remove_if(result.begin(), result.end(),
                       [&](const std::vector<int> &v) { return v.empty(); }),
        result.end());
    return result;
  }

private:
  int _n;
  // root node: -1 * component size
  // otherwise: parent
  std::vector<int> parent_or_size;
};
// =======================================

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  ll N;
  cin >> N;
  dsu d(N);
  vector<vector<ll>> P(N);
  set<ll> y_remain;
  vector<ll> X(N), Y(N);
  vector<ll> y_to_index(N + 1);
  for (ll i = 0; i < N; ++i) {
    cin >> X.at(i) >> Y.at(i);
    P.at(i) = {X.at(i), Y.at(i), i};
    y_to_index.at(Y.at(i)) = i;
    y_remain.insert(Y.at(i));
  }
  sort(P.begin(), P.end());
  for (ll i = 0; i < N; ++i) {
    vector<ll> now = P.at(i);
    ll x = now.at(0), y = now.at(1), index = now.at(2);
    auto itr1 = y_remain.lower_bound(y);
    if (itr1 == y_remain.end() || *itr1 != y) {
      continue;
    }
    y_remain.erase(y);
    auto itr = y_remain.lower_bound(y);
    while (itr != y_remain.end()) {
      ll temp_y = *itr;
      if (d.same(y_to_index.at(y), y_to_index.at(temp_y))) {
        break;
      }
      d.merge(y_to_index.at(y), y_to_index.at(temp_y));
      // cout << y_to_index.at(y) << ' ' << y_to_index.at(temp_y) << "\\n";
      itr++;
    }
  }
  for (ll i = 0; i < N; ++i) {
    cout << d.size(i) << "\\n";
  }
}


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

int n;
vector<pair<pair<int, int>, int>> xy;

// 素集合データ構造
//  ref:https://ei1333.github.io/luzhiled/snippets/structure/union-find.html
struct union_find {
  // parent[v]==w(>=0):vの親はw
  // parent[v]==k(<0):vは根&&vの属するグループの要素数は-k
  vector<long long int> parent;
  // コンストラクタ
  union_find(long long int n) { parent.assign(n, -1); }
  // vの根を求める
  // 求めると同時に親を根に置き換える(木の圧縮)
  long long int root(long long int v) {
    if (parent[v] < 0) {
      return v;
    } else {
      parent[v] = root(parent[v]);
      return parent[v];
    }
  }
  // vが属するグループの要素数を求める
  long long int size(long long int v) { return -parent[root(v)]; }
  // vとwを繋げる
  void unite(long long int v, long long int w) {
    v = root(v);
    w = root(w);
    if (v == w)
      return;
    if (parent[v] < parent[w])
      swap(v, w);
    parent[w] += parent[v];
    parent[v] = w;
  }
  // vとwが同値か判定
  bool isSame(long long int v, long long int w) { return root(v) == root(w); }
};

int main() {
  cin >> n;
  xy.resize(n);
  for (int i = 0; i < n; i++) {
    cin >> xy[i].first.first >> xy[i].first.second;
    xy[i].second = i;
  }
  sort(xy.begin(), xy.end());
  union_find uf(n);
  // first:y,second:index
  set<pair<int, int>> st;
  for (int i = 0; i < n; i++) {
    vector<pair<int, int>> connected;
    for (auto e : st) {
      if (e.first < xy[i].first.second) {
        uf.unite(e.second, i);
        connected.push_back(e);
      }
    }
    if (connected.empty()) {
      st.insert({xy[i].first.second, i});
    } else {
      pair<int, int> repr = {INT_MAX, -1};
      for (auto g : connected) {
        if (repr.first > g.first) {
          repr = g;
        }
      }
      for (auto g : connected) {
        if (g != repr) {
          st.erase(g);
        }
      }
    }
  }
  vector<int> ans(n);
  for (int i = 0; i < n; i++) {
    ans[xy[i].second] = uf.size(i);
  }
  for (int i = 0; i < n; i++) {
    cout << ans[i] << endl;
  }
}

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

int n;
vector<pair<pair<int, int>, int>> xy;

// 素集合データ構造
//  ref:https://ei1333.github.io/luzhiled/snippets/structure/union-find.html
struct union_find {
  // parent[v]==w(>=0):vの親はw
  // parent[v]==k(<0):vは根&&vの属するグループの要素数は-k
  vector<long long int> parent;
  // コンストラクタ
  union_find(long long int n) { parent.assign(n, -1); }
  // vの根を求める
  // 求めると同時に親を根に置き換える(木の圧縮)
  long long int root(long long int v) {
    if (parent[v] < 0) {
      return v;
    } else {
      parent[v] = root(parent[v]);
      return parent[v];
    }
  }
  // vが属するグループの要素数を求める
  long long int size(long long int v) { return -parent[root(v)]; }
  // vとwを繋げる
  void unite(long long int v, long long int w) {
    v = root(v);
    w = root(w);
    if (v == w)
      return;
    if (parent[v] < parent[w])
      swap(v, w);
    parent[w] += parent[v];
    parent[v] = w;
  }
  // vとwが同値か判定
  bool isSame(long long int v, long long int w) { return root(v) == root(w); }
};

int main() {
  cin >> n;
  xy.resize(n);
  for (int i = 0; i < n; i++) {
    cin >> xy[i].first.first >> xy[i].first.second;
    xy[i].second = i;
  }
  sort(xy.begin(), xy.end());
  union_find uf(n);
  // first:y,second:index
  set<pair<int, int>> st;
  for (int i = 0; i < n; i++) {
    vector<pair<int, int>> connected;
    for (auto e : st) {
      if (e.first < xy[i].first.second) {
        uf.unite(e.second, i);
        connected.push_back(e);
      } else {
        break;
      }
    }
    if (connected.empty()) {
      st.insert({xy[i].first.second, i});
    } else {
      pair<int, int> repr = {INT_MAX, -1};
      for (auto g : connected) {
        if (repr.first > g.first) {
          repr = g;
        }
      }
      for (auto g : connected) {
        if (g != repr) {
          st.erase(g);
        }
      }
    }
  }
  vector<int> ans(n);
  for (int i = 0; i < n; i++) {
    ans[xy[i].second] = uf.size(i);
  }
  for (int i = 0; i < n; i++) {
    cout << ans[i] << endl;
  }
}


// This Code was made by Chinese_zjc_.
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
// #include<windows.h>
#define int long long
#define double long double
using namespace std;
const double PI = acos(-1);
const double eps = 0.0000000001;
const int INF = 0x3fffffffffffffff;
int n, m, id[55][55], cnt, tim, head[6005], s, t, tot = 1, dis[6005], ans, cost,
                                                  NOW[6005];
bool in[6005];
queue<int> que;
struct node {
  int x, y;
  bool can[55][55];
  node(int X = 0, int Y = 0) {
    x = X;
    y = Y;
    memset(can, false, sizeof can);
  }
} g[105];
struct edge {
  int cost, cap, nxt, to;
} e[50005];
char a[55][55];
void add(int f, int t, int cost, int cap) {
  e[++tot] = {cost, cap, head[f], t};
  head[f] = tot;
}
bool spfa() {
  fill(dis + 1, dis + 1 + tim, -INF);
  dis[s] = 0;
  in[s] = true;
  que.push(s);
  while (!que.empty()) {
    int now = que.front();
    in[now] = false;
    que.pop();
    for (int i = head[now]; i; i = e[i].nxt) {
      if (e[i].cap && dis[e[i].to] < dis[now] + e[i].cost) {
        dis[e[i].to] = dis[now] + e[i].cost;
        if (!in[e[i].to]) {
          in[e[i].to] = true;
          que.push(e[i].to);
        }
      }
    }
  }
  return dis[t] != -INF;
}
int dfs(int now, int v) {
  // cout << now << ' ' << v << ' ' << sta << endl;
  if (now == t) {
    ans += v;
    return v;
  }
  in[now] = true;
  int res = 0;
  for (int i = NOW[now]; i && v; i = e[i].nxt) {
    NOW[now] = i;
    if (!in[e[i].to] && e[i].cap && dis[now] + e[i].cost == dis[e[i].to]) {
      int tmp = dfs(e[i].to, min(v, e[i].cap));
      e[i ^ 0].cap -= tmp;
      e[i ^ 1].cap += tmp;
      v -= tmp;
      res += tmp;
      cost += e[i].cost * tmp;
    }
  }
  in[now] = false;
  return res;
}
signed main() {
  ios::sync_with_stdio(false);
  cin >> n >> m;
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      cin >> a[i][j];
    }
  }
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      if (a[i][j] == 'o') {
        g[++cnt] = {i, j};
      }
    }
  }
  for (int k = 1; k <= cnt; ++k) {
    g[k].can[g[k].x][g[k].y] = true;
    for (int i = g[k].x; i <= n; ++i) {
      for (int j = g[k].y; j <= m; ++j) {
        g[k].can[i][j] |=
            (a[i][j] != '#' && (g[k].can[i - 1][j] || g[k].can[i][j - 1]));
      }
    }
  }
  for (int i = 1; i <= cnt; ++i) {
    ++tim;
  }
  s = ++tim;
  t = ++tim;
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      id[i][j] = ++tim;
      add(id[i][j], t, 0, 1);
      add(t, id[i][j], 0, 0);
    }
  }
  for (int i = 1; i <= cnt; ++i) {
    add(s, i, 0, 1);
    add(i, s, 0, 0);
    for (int j = g[i].x; j <= n; ++j) {
      for (int k = g[i].y; k <= m; ++k) {
        if (g[i].can[j][k]) {
          add(i, id[j][k], +(j - g[i].x) + (k - g[i].y), 1);
          add(id[j][k], i, -(j - g[i].x) - (k - g[i].y), 0);
        }
      }
    }
  }
  while (spfa()) {
    for (int i = 1; i <= tim; ++i) {
      NOW[i] = head[i];
    }
    int l = dfs(s, INF);
    while (l) {
      l = dfs(s, INF);
      // getchar();
    }
  }
  cout << cost << endl;
  return 0;
}

// This Code was made by Chinese_zjc_.
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
// #include<windows.h>
#define int long long
#define double long double
using namespace std;
const double PI = acos(-1);
const double eps = 0.0000000001;
const int INF = 0x3fffffffffffffff;
int n, m, id[55][55], cnt, tim, head[6005], s, t, tot = 1, dis[6005], ans, cost,
                                                  NOW[6005];
bool in[6005];
queue<int> que;
struct node {
  int x, y;
  bool can[55][55];
  node(int X = 0, int Y = 0) {
    x = X;
    y = Y;
    memset(can, false, sizeof can);
  }
} g[105];
struct edge {
  int cost, cap, nxt, to;
} e[500005];
char a[55][55];
void add(int f, int t, int cost, int cap) {
  e[++tot] = {cost, cap, head[f], t};
  head[f] = tot;
}
bool spfa() {
  fill(dis + 1, dis + 1 + tim, -INF);
  dis[s] = 0;
  in[s] = true;
  que.push(s);
  while (!que.empty()) {
    int now = que.front();
    in[now] = false;
    que.pop();
    for (int i = head[now]; i; i = e[i].nxt) {
      if (e[i].cap && dis[e[i].to] < dis[now] + e[i].cost) {
        dis[e[i].to] = dis[now] + e[i].cost;
        if (!in[e[i].to]) {
          in[e[i].to] = true;
          que.push(e[i].to);
        }
      }
    }
  }
  return dis[t] != -INF;
}
int dfs(int now, int v) {
  // cout << now << ' ' << v << ' ' << sta << endl;
  if (now == t) {
    ans += v;
    return v;
  }
  in[now] = true;
  int res = 0;
  for (int i = NOW[now]; i && v; i = e[i].nxt) {
    NOW[now] = i;
    if (!in[e[i].to] && e[i].cap && dis[now] + e[i].cost == dis[e[i].to]) {
      int tmp = dfs(e[i].to, min(v, e[i].cap));
      e[i ^ 0].cap -= tmp;
      e[i ^ 1].cap += tmp;
      v -= tmp;
      res += tmp;
      cost += e[i].cost * tmp;
    }
  }
  in[now] = false;
  return res;
}
signed main() {
  ios::sync_with_stdio(false);
  cin >> n >> m;
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      cin >> a[i][j];
    }
  }
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      if (a[i][j] == 'o') {
        g[++cnt] = {i, j};
      }
    }
  }
  for (int k = 1; k <= cnt; ++k) {
    g[k].can[g[k].x][g[k].y] = true;
    for (int i = g[k].x; i <= n; ++i) {
      for (int j = g[k].y; j <= m; ++j) {
        g[k].can[i][j] |=
            (a[i][j] != '#' && (g[k].can[i - 1][j] || g[k].can[i][j - 1]));
      }
    }
  }
  for (int i = 1; i <= cnt; ++i) {
    ++tim;
  }
  s = ++tim;
  t = ++tim;
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      id[i][j] = ++tim;
      add(id[i][j], t, 0, 1);
      add(t, id[i][j], 0, 0);
    }
  }
  for (int i = 1; i <= cnt; ++i) {
    add(s, i, 0, 1);
    add(i, s, 0, 0);
    for (int j = g[i].x; j <= n; ++j) {
      for (int k = g[i].y; k <= m; ++k) {
        if (g[i].can[j][k]) {
          add(i, id[j][k], +(j - g[i].x) + (k - g[i].y), 1);
          add(id[j][k], i, -(j - g[i].x) - (k - g[i].y), 0);
        }
      }
    }
  }
  while (spfa()) {
    for (int i = 1; i <= tim; ++i) {
      NOW[i] = head[i];
    }
    int l = dfs(s, INF);
    while (l) {
      l = dfs(s, INF);
      // getchar();
    }
  }
  cout << cost << endl;
  return 0;
}


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

// Start
typedef long long ll;
typedef double db;
#define mp(a, b) make_pair((a), (b))
#define x first
#define y second
#define be(a) (a).begin()
#define en(a) (a).end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
#define R(i, a, b) for (int i = (a), I = (b); i < I; i++)
#define L(i, a, b) for (int i = (b)-1, I = (a)-1; i > I; i--)
const int iinf = 0x3f3f3f3f;
const ll linf = 0x3f3f3f3f3f3f3f3f;

// NetWorkFlow
const int tN = 252;
int tn, s, t;
vector<int> e[tN], to, fw, co;
void adde(int u, int v, int w, int c) {
  e[u].pb(sz(to)), to.pb(v), fw.pb(w), co.pb(+c);
  e[v].pb(sz(to)), to.pb(u), fw.pb(0), co.pb(-c);
}
bool vis[tN];
int pre[tN], dep[tN];
bool spfa() {
  fill(vis, vis + tn, false);
  fill(pre, pre + tn, -1);
  fill(dep, dep + tn, -iinf);
  queue<int> q;
  q.push(s), dep[s] = 0, vis[s] = true;
  while (sz(q)) {
    int u = q.front();
    q.pop(), vis[u] = false;
    for (int v : e[u])
      if (fw[v] && dep[to[v]] < dep[u] + co[v]) {
        dep[to[v]] = dep[u] + co[v], pre[to[v]] = v;
        if (!vis[to[v]])
          vis[to[v]] = true, q.push(to[v]);
      }
  }
  return dep[t] != -iinf;
}
pair<int, int> mcmf() {
  pair<int, int> res(0, 0);
  while (spfa()) {
    //		cout<<"lycfucking.\\n";
    int f = iinf;
    for (int i = t; i != s; i = to[pre[i] ^ 1])
      f = min(f, fw[pre[i]]);
    for (int i = t; i != s; i = to[pre[i] ^ 1])
      fw[pre[i]] -= f, fw[pre[i] ^ 1] += f;
    res.x += f, res.y += dep[t] * f;
    //		cout<<"fengyangyang\\n";
  }
  return res;
}

// Data
const int N = 50;
int n, m;
char Map[N][N];
#define p(x, y) ((x) * (m) + (y))

// Main
int main() {
  //	ios::sync_with_stdio(0);
  //	cin.tie(0),cout.tie(0);
  cin >> n >> m;
  tn = (t = (s = n * m) + 1) + 1;
  R(i, 0, n) cin >> Map[i];
  R(i, 0, n) R(j, 0, m) if (Map[i][j] != '#') {
    if (Map[i][j] == 'o')
      adde(s, p(i, j), 1, 0);
    if (i + 1 < n && Map[i + 1][j] != '#')
      adde(p(i, j), p(i + 1, j), iinf, 1);
    if (j + 1 < m && Map[i][j + 1] != '#')
      adde(p(i, j), p(i, j + 1), iinf, 1);
    adde(p(i, j), t, 1, 0);
  }
  //	cout<<"?\\n";
  pair<int, int> ans = mcmf();
  //	cout<<"!\\n";
  cout << ans.y << '\\n';
  return 0;
}

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

// Start
typedef long long ll;
typedef double db;
#define mp(a, b) make_pair((a), (b))
#define x first
#define y second
#define be(a) (a).begin()
#define en(a) (a).end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
#define R(i, a, b) for (int i = (a), I = (b); i < I; i++)
#define L(i, a, b) for (int i = (b)-1, I = (a)-1; i > I; i--)
const int iinf = 0x3f3f3f3f;
const ll linf = 0x3f3f3f3f3f3f3f3f;

// NetWorkFlow
const int tN = 10002;
int tn, s, t;
vector<int> e[tN], to, fw, co;
void adde(int u, int v, int w, int c) {
  e[u].pb(sz(to)), to.pb(v), fw.pb(w), co.pb(+c);
  e[v].pb(sz(to)), to.pb(u), fw.pb(0), co.pb(-c);
}
bool vis[tN];
int pre[tN], dep[tN];
bool spfa() {
  fill(vis, vis + tn, false);
  fill(pre, pre + tn, -1);
  fill(dep, dep + tn, -iinf);
  queue<int> q;
  q.push(s), dep[s] = 0, vis[s] = true;
  while (sz(q)) {
    int u = q.front();
    q.pop(), vis[u] = false;
    for (int v : e[u])
      if (fw[v] && dep[to[v]] < dep[u] + co[v]) {
        dep[to[v]] = dep[u] + co[v], pre[to[v]] = v;
        if (!vis[to[v]])
          vis[to[v]] = true, q.push(to[v]);
      }
  }
  return dep[t] != -iinf;
}
pair<int, int> mcmf() {
  pair<int, int> res(0, 0);
  while (spfa()) {
    //		cout<<"lycfucking.\\n";
    int f = iinf;
    for (int i = t; i != s; i = to[pre[i] ^ 1])
      f = min(f, fw[pre[i]]);
    for (int i = t; i != s; i = to[pre[i] ^ 1])
      fw[pre[i]] -= f, fw[pre[i] ^ 1] += f;
    res.x += f, res.y += dep[t] * f;
    //		cout<<"fengyangyang\\n";
  }
  return res;
}

// Data
const int N = 100;
int n, m;
char Map[N][N];
#define p(x, y) ((x) * (m) + (y))

// Main
int main() {
  //	ios::sync_with_stdio(0);
  //	cin.tie(0),cout.tie(0);
  cin >> n >> m;
  tn = (t = (s = n * m) + 1) + 1;
  R(i, 0, n) cin >> Map[i];
  R(i, 0, n) R(j, 0, m) if (Map[i][j] != '#') {
    if (Map[i][j] == 'o')
      adde(s, p(i, j), 1, 0);
    if (i + 1 < n && Map[i + 1][j] != '#')
      adde(p(i, j), p(i + 1, j), iinf, 1);
    if (j + 1 < m && Map[i][j + 1] != '#')
      adde(p(i, j), p(i, j + 1), iinf, 1);
    adde(p(i, j), t, 1, 0);
  }
  //	cout<<"?\\n";
  pair<int, int> ans = mcmf();
  //	cout<<"!\\n";
  cout << ans.y << '\\n';
  return 0;
}


#include <bits/stdc++.h>

using namespace std;
#define pb push_back
#define all(x) x.begin(), x.end()
#define sz(x) (int)x.size()
using ll = long long;
using pii = pair<int, int>;
using ull = unsigned long long;

const int mn = 50;

namespace Min_Cost_Max_Flow {
const int INF = 0x3f3f3f3f; // INF flow
const int N = 2505;         // 最大点数
const int M = 40000;        // 最大边数
int S, T, cnt, from[N], g[N], flow;
ll d[N]; // 这里的 flow 就是最大流量
ll ans;  // 费用，即为答案
struct edge {
  int from, to, nxt, v;
  ll c;
} e[M]; // c 是价格 v 是流量
void add(int u, int v, int w, ll c) {
  e[++cnt].from = u;
  e[cnt].to = v;
  e[cnt].nxt = g[u];
  g[u] = cnt;
  e[cnt].c = c;
  e[cnt].v = w;
}

void add_edge(int u, int v, int w, ll c) {
  // printf("add_edge %d %d %d %lld\\n",u,v,w,c);
  add(u, v, w, c);
  add(v, u, 0, -c);
}

bool spfa() {
  fill_n(d, N, INF);
  d[S] = 0;
  memset(from, 0, sizeof(from));
  queue<int> q;
  q.push(S);
  while (!q.empty()) {
    int now = q.front();
    q.pop();
    for (int i = g[now]; i; i = e[i].nxt) {
      if (e[i].v && d[e[i].to] > d[now] + e[i].c) {
        d[e[i].to] = d[now] + e[i].c;
        from[e[i].to] = i;
        q.push(e[i].to);
      }
    }
  }
  return (d[T] != INF);
  // 改为return(d[T]<=0)即为最小费用可行流
}

void mcf() {
  int x = INF;
  for (int i = from[T]; i; i = from[e[i].from])
    x = min(x, e[i].v);
  flow += x;
  for (int i = from[T]; i; i = from[e[i].from]) {
    e[i].v -= x;
    e[i ^ 1].v += x;
    ans += e[i].c * x;
  }
}

void Initialize(int s, int t) { // 源点和汇点
  S = s;
  T = t;
  memset(g, 0, sizeof(g));
  memset(e, 0, sizeof(e));
  ans = flow = 0;
  cnt = 1;
}

void doit() {
  while (spfa())
    mcf();
}
} // namespace Min_Cost_Max_Flow

int n, m;
char s[mn][mn];

int getid(int x, int y) { return (x - 1) * m + y; }

int main() {
  // cin.sync_with_stdio(0);
#ifdef trote
  freopen("../1.txt", "r", stdin);
  // freopen("../2.txt", "w", stdout);
  auto bg = clock();
#endif

  using namespace Min_Cost_Max_Flow;
  scanf("%d%d", &n, &m);
  Initialize(0, n * m + 1);
  for (int i = 1; i <= n; i++) {
    scanf("%s", s[i] + 1);
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      if (s[i][j] == '#')
        continue;
      if (s[i][j] == 'o') {
        add_edge(0, getid(i, j), 1, 0);
      }
      if (i < n && s[i + 1][j] != '#') {
        add_edge(getid(i, j), getid(i + 1, j), INF, -1);
      }
      if (j < m && s[i][j + 1] != '#') {
        add_edge(getid(i, j), getid(i, j + 1), INF, -1);
      }
      add_edge(getid(i, j), n * m + 1, 1, 0);
    }
  }
  doit();
  printf("%lld\\n", -ans);
}

#include <bits/stdc++.h>

using namespace std;
#define pb push_back
#define all(x) x.begin(), x.end()
#define sz(x) (int)x.size()
using ll = long long;
using pii = pair<int, int>;
using ull = unsigned long long;

const int mn = 55;

namespace Min_Cost_Max_Flow {
const int INF = 0x3f3f3f3f; // INF flow
const int N = 2503;         // 最大点数
const int M = 50000;        // 最大边数
int S, T, cnt, from[N], g[N], flow;
ll d[N]; // 这里的 flow 就是最大流量
ll ans;  // 费用，即为答案
struct edge {
  int from, to, nxt, v;
  ll c;
} e[M]; // c 是价格 v 是流量
void add(int u, int v, int w, ll c) {
  e[++cnt].from = u;
  e[cnt].to = v;
  e[cnt].nxt = g[u];
  g[u] = cnt;
  e[cnt].c = c;
  e[cnt].v = w;
}

void add_edge(int u, int v, int w, ll c) {
  // printf("add_edge %d %d %d %lld\\n", u, v, w, c);
  add(u, v, w, c);
  add(v, u, 0, -c);
}

bool spfa() {
  fill_n(d, N, INF);
  d[S] = 0;
  memset(from, 0, sizeof(from));
  queue<int> q;
  q.push(S);
  while (!q.empty()) {
    int now = q.front();
    q.pop();
    for (int i = g[now]; i; i = e[i].nxt) {
      if (e[i].v && d[e[i].to] > d[now] + e[i].c) {
        d[e[i].to] = d[now] + e[i].c;
        from[e[i].to] = i;
        q.push(e[i].to);
      }
    }
  }
  return (d[T] != INF);
  // 改为return(d[T]<=0)即为最小费用可行流
}

void mcf() {
  int x = INF;
  for (int i = from[T]; i; i = from[e[i].from])
    x = min(x, e[i].v);
  flow += x;
  for (int i = from[T]; i; i = from[e[i].from]) {
    e[i].v -= x;
    e[i ^ 1].v += x;
    ans += e[i].c * x;
  }
}

void Initialize(int s, int t) { // 源点和汇点
  S = s;
  T = t;
  memset(g, 0, sizeof(g));
  memset(e, 0, sizeof(e));
  ans = flow = 0;
  cnt = 1;
}

void doit() {
  while (spfa())
    mcf();
}
} // namespace Min_Cost_Max_Flow

int n, m;
char s[mn][mn];

int getid(int x, int y) { return (x - 1) * m + y; }

int vis[mn][mn];
int to_end[mn][mn];

int head_count = 0;

void calc(int x, int y) {
  struct node {
    int x, y, t;

    bool operator<(const node &p) const { return t > p.t; }
  };
  memset(vis, 0, sizeof vis);
  queue<node> q;
  q.push({x, y, 0});
  vector<node> v;
  while (!q.empty()) {
    node u = q.front();
    q.pop();
    // printf("%d %d\\n", u.x, u.y);
    v.push_back(u);
    if (u.x < n) {
      node nxt = {u.x + 1, u.y, u.t + 1};
      if (!(vis[nxt.x][nxt.y] || s[nxt.x][nxt.y] == '#')) {
        vis[nxt.x][nxt.y] = 1;
        q.push(nxt);
      }
    }
    if (u.y < m) {
      node nxt = {u.x, u.y + 1, u.t + 1};
      if (vis[nxt.x][nxt.y] || s[nxt.x][nxt.y] == '#')
        continue;
      vis[nxt.x][nxt.y] = 1;
      q.push(nxt);
    }
  }
  sort(begin(v), end(v));

  using namespace Min_Cost_Max_Flow;
  for (int i = 0; i < v.size() && i < head_count; i++) {
    node &p = v[i];
    if (p.t == 0)
      break;
    // printf("%d %d -> %d %d : %d\\n", x, y, p.x, p.y, p.t);
    add_edge(getid(x, y), getid(p.x, p.y), 1, -p.t);
    if (!to_end[p.x][p.y]) {
      to_end[p.x][p.y] = 1;
      add_edge(getid(p.x, p.y), n * m + 1, 1, 0);
    }
  }
  if (!to_end[x][y]) {
    to_end[x][y] = 1;
    add_edge(getid(x, y), n * m + 1, 1, 0);
  }
}

int main() {
  // cin.sync_with_stdio(0);
#ifdef trote
  freopen("../1.txt", "r", stdin);
  // freopen("../2.txt", "w", stdout);
  auto bg = clock();
#endif

  using namespace Min_Cost_Max_Flow;
  scanf("%d%d", &n, &m);
  Initialize(0, n * m + 1);
  for (int i = 1; i <= n; i++) {
    scanf("%s", s[i] + 1);
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      if (s[i][j] == 'o') {
        add_edge(0, getid(i, j), 1, 0);
        head_count++;
      }
    }
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      if (s[i][j] == 'o') {
        calc(i, j);
      }
    }
  }
  doit();
  printf("%lld\\n", -ans);
}


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
template <typename flow_t = int, typename cost_t = int> struct mcSFlow {
  struct Edge {
    cost_t c;
    flow_t f;
    int to, rev;
    Edge(int _to, cost_t _c, flow_t _f, int _rev)
        : c(_c), f(_f), to(_to), rev(_rev) {}
  };
  static constexpr cost_t INFCOST = numeric_limits<cost_t>::max() / 2;
  cost_t eps;
  int N, S, T;
  vector<vector<Edge>> G;
  vector<int> isq, cur;
  vector<flow_t> ex;
  vector<cost_t> h;
  void init(int _N, int _S, int _T) {
    eps = 0;
    N = _N;
    S = _S;
    T = _T;
    G.resize(_N);
  }
  Edge add_edge(int a, int b, flow_t cap, cost_t cost) {
    assert(cap >= 0);
    assert(a >= 0 && a < N && b >= 0 && b < N);
    assert(a != b);
    cost *= N;
    eps = max(eps, abs(cost));
    G[a].emplace_back(b, cost, cap, G[b].size());
    Edge ret = G[a].back();
    G[b].emplace_back(a, -cost, 0, G[a].size() - 1);
    return ret;
  }
  void add_flow(Edge &e, flow_t f) {
    Edge &back = G[e.to][e.rev];
    if (!ex[e.to] && f)
      hs[h[e.to]].push_back(e.to);
    e.f -= f;
    ex[e.to] += f;
    back.f += f;
    ex[back.to] -= f;
  }
  vector<vector<int>> hs;
  vector<int> co;
  // fast max flow, lowest label version
  flow_t max_flow() {
    ex.assign(N, 0);
    h.assign(N, 0);
    hs.resize(2 * N);
    co.assign(2 * N, 0);
    cur.assign(N, 0);
    h[S] = N;
    ex[T] = 1;
    co[0] = N - 1;
    for (auto &e : G[S])
      add_flow(e, e.f);
    if (hs[0].size())
      for (int hi = 0; hi >= 0;) {
        int u = hs[hi].back();
        hs[hi].pop_back();
        while (ex[u] > 0) { // discharge u
          if (cur[u] == G[u].size()) {
            h[u] = 1e9;
            for (int i = 0; i < G[u].size(); ++i) {
              auto &e = G[u][i];
              if (e.f && h[u] > h[e.to] + 1) {
                h[u] = h[e.to] + 1;
                cur[u] = i;
              }
            }
            if (++co[h[u]], !--co[hi] && hi < N)
              for (int i = 0; i < N; ++i) {
                if (hi < h[i] && h[i] < N) {
                  --co[h[i]];
                  h[i] = N + 1;
                }
              }
            hi = h[u];
          } else if (G[u][cur[u]].f && h[u] == h[G[u][cur[u]].to] + 1) {
            add_flow(G[u][cur[u]], min(ex[u], G[u][cur[u]].f));
          } else
            ++cur[u];
        }
        while (hi >= 0 && hs[hi].empty())
          --hi;
      }
    return -ex[S];
  }
  // begin min cost flow
  bool look_ahead(int u) {
    if (ex[u])
      return false;
    cost_t newHeight = h[u] - N * eps;
    for (auto const &e : G[u]) {
      if (e.f == 0)
        continue;
      if (h[u] + e.c - h[e.to] < 0)
        return false; // outgoing admissible arc
      else
        newHeight = max(newHeight, h[e.to] - e.c); // try to make arc admissible
    }
    h[u] = newHeight - eps;
    return true;
  }
  void push(Edge &e, flow_t amt) {
    if (e.f < amt)
      amt = e.f;
    e.f -= amt;
    ex[e.to] += amt;
    G[e.to][e.rev].f += amt;
    ex[G[e.to][e.rev].to] -= amt;
  }
  void relabel(int vertex) {
    cost_t newHeight = -INFCOST;
    for (int i = 0; i < G[vertex].size(); ++i) {
      Edge const &e = G[vertex][i];
      if (e.f && newHeight < h[e.to] - e.c) {
        newHeight = h[e.to] - e.c;
        cur[vertex] = i;
      }
    }
    h[vertex] = newHeight - eps;
  }
  static constexpr int scale = 2;
  template <bool use_look_ahead = true> pair<flow_t, cost_t> minCostMaxFlow() {
    cost_t retCost = 0;
    for (int i = 0; i < N; ++i)
      for (Edge &e : G[i])
        retCost += e.c * (e.f);
    // remove this for circulation
    flow_t retFlow = max_flow();
    h.assign(N, 0);
    ex.assign(N, 0);
    isq.assign(N, 0);
    cur.assign(N, 0);
    stack<int> q;
    for (; eps; eps >>= scale) {
      fill(cur.begin(), cur.end(), 0);
      for (int i = 0; i < N; ++i)
        for (auto &e : G[i])
          if (h[i] + e.c - h[e.to] < 0 && e.f)
            push(e, e.f);
      for (int i = 0; i < N; ++i) {
        if (ex[i] > 0) {
          q.push(i);
          isq[i] = 1;
        }
      }
      while (!q.empty()) {
        int u = q.top();
        q.pop();
        isq[u] = 0;
        while (ex[u] > 0) {
          if (cur[u] == G[u].size())
            relabel(u);
          for (int &i = cur[u], max_i = G[u].size(); i < max_i; ++i) {
            Edge &e = G[u][i];
            if (e.f == 0)
              continue;
            if (h[u] + e.c - h[e.to] < 0) {
              if (use_look_ahead && look_ahead(e.to)) {
                --i;
                continue;
              }
              push(e, ex[u]);
              if (isq[e.to] == 0) {
                q.push(e.to);
                isq[e.to] = 1;
              }
              if (ex[u] == 0)
                break;
            }
          }
        }
      }
      if (eps > 1 && eps >> scale == 0) {
        eps = 1 << scale;
      }
    }
    for (int i = 0; i < N; ++i) {
      for (Edge &e : G[i]) {
        retCost -= e.c * (e.f);
      }
    }
    return make_pair(retFlow, retCost / 2 / N);
  }
  flow_t getFlow(Edge const &e) { return G[e.to][e.rev].f; }
};

const int N = 50 + 7;
int n;
int m;
int a[N][N];
bool vis[N][N];
vector<int> reach[N][N];

int id(int r, int c) { return c + (r - 1) * m - 1; }

void gol() {
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      vis[i][j] = 0;
    }
  }
}

int cnt;
int r;
int c;

mcSFlow<int, int> f;

void dfs(int i, int j) {
  vis[i][j] = 1;
  f.add_edge(1 + id(r, c), 1 + n * m + id(i, j), 1, -(i + j - r - c));
  if (a[i + 1][j] != -1 && !vis[i + 1][j]) {
    dfs(i + 1, j);
  }
  if (a[i][j + 1] != -1 && !vis[i][j + 1]) {
    dfs(i, j + 1);
  }
}

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

  int sum = 0;
  cin >> n >> m;
  f.init(1 + n * m + n * m + 1, 0, 1 + n * m + n * m);
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      a[i][j] = -1;
    }
  }
  for (int i = 1; i <= n; i++) {
    string s;
    cin >> s;
    for (int j = 1; j <= m; j++) {
      if (s[j - 1] == '.') {
        a[i][j] = 0;
      }
      if (s[j - 1] == 'o') {
        a[i][j] = 1;
      }
      if (s[j - 1] == '#') {
        a[i][j] = -1;
      }
    }
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      if (a[i][j] == 1) {
        r = i;
        c = j;
        cnt++;
        sum = i + j;
        gol();
        dfs(i, j);
        f.add_edge(0, 1 + id(i, j), 1, 0);
      }
    }
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      f.add_edge(1 + n * m + id(i, j), 1 + n * m + n * m, 1, 0);
    }
  }
  pair<int, int> it = f.minCostMaxFlow();
  cout << -it.second << "\\n";

  /// cout << f.minCostMaxFlow(0, 1 + n * m + n * m).second << "\\n";
}

#include <bits/stdc++.h>

using namespace std;

#define int long long

template <typename flow_t = int, typename cost_t = int> struct mcSFlow {
  struct Edge {
    cost_t c;
    flow_t f;
    int to, rev;
    Edge(int _to, cost_t _c, flow_t _f, int _rev)
        : c(_c), f(_f), to(_to), rev(_rev) {}
  };
  static constexpr cost_t INFCOST = numeric_limits<cost_t>::max() / 2;
  cost_t eps;
  int N, S, T;
  vector<vector<Edge>> G;
  vector<int> isq, cur;
  vector<flow_t> ex;
  vector<cost_t> h;
  void init(int _N, int _S, int _T) {
    eps = 0;
    N = _N;
    S = _S;
    T = _T;
    G.resize(_N);
  }
  Edge add_edge(int a, int b, flow_t cap, cost_t cost) {
    assert(cap >= 0);
    assert(a >= 0 && a < N && b >= 0 && b < N);
    assert(a != b);
    cost *= N;
    eps = max(eps, abs(cost));
    G[a].emplace_back(b, cost, cap, G[b].size());
    Edge ret = G[a].back();
    G[b].emplace_back(a, -cost, 0, G[a].size() - 1);
    return ret;
  }
  void add_flow(Edge &e, flow_t f) {
    Edge &back = G[e.to][e.rev];
    if (!ex[e.to] && f)
      hs[h[e.to]].push_back(e.to);
    e.f -= f;
    ex[e.to] += f;
    back.f += f;
    ex[back.to] -= f;
  }
  vector<vector<int>> hs;
  vector<int> co;
  // fast max flow, lowest label version
  flow_t max_flow() {
    ex.assign(N, 0);
    h.assign(N, 0);
    hs.resize(2 * N);
    co.assign(2 * N, 0);
    cur.assign(N, 0);
    h[S] = N;
    ex[T] = 1;
    co[0] = N - 1;
    for (auto &e : G[S])
      add_flow(e, e.f);
    if (hs[0].size())
      for (int hi = 0; hi >= 0;) {
        int u = hs[hi].back();
        hs[hi].pop_back();
        while (ex[u] > 0) { // discharge u
          if (cur[u] == G[u].size()) {
            h[u] = 1e9;
            for (int i = 0; i < G[u].size(); ++i) {
              auto &e = G[u][i];
              if (e.f && h[u] > h[e.to] + 1) {
                h[u] = h[e.to] + 1;
                cur[u] = i;
              }
            }
            if (++co[h[u]], !--co[hi] && hi < N)
              for (int i = 0; i < N; ++i) {
                if (hi < h[i] && h[i] < N) {
                  --co[h[i]];
                  h[i] = N + 1;
                }
              }
            hi = h[u];
          } else if (G[u][cur[u]].f && h[u] == h[G[u][cur[u]].to] + 1) {
            add_flow(G[u][cur[u]], min(ex[u], G[u][cur[u]].f));
          } else
            ++cur[u];
        }
        while (hi >= 0 && hs[hi].empty())
          --hi;
      }
    return -ex[S];
  }
  // begin min cost flow
  bool look_ahead(int u) {
    if (ex[u])
      return false;
    cost_t newHeight = h[u] - N * eps;
    for (auto const &e : G[u]) {
      if (e.f == 0)
        continue;
      if (h[u] + e.c - h[e.to] < 0)
        return false; // outgoing admissible arc
      else
        newHeight = max(newHeight, h[e.to] - e.c); // try to make arc admissible
    }
    h[u] = newHeight - eps;
    return true;
  }
  void push(Edge &e, flow_t amt) {
    if (e.f < amt)
      amt = e.f;
    e.f -= amt;
    ex[e.to] += amt;
    G[e.to][e.rev].f += amt;
    ex[G[e.to][e.rev].to] -= amt;
  }
  void relabel(int vertex) {
    cost_t newHeight = -INFCOST;
    for (int i = 0; i < G[vertex].size(); ++i) {
      Edge const &e = G[vertex][i];
      if (e.f && newHeight < h[e.to] - e.c) {
        newHeight = h[e.to] - e.c;
        cur[vertex] = i;
      }
    }
    h[vertex] = newHeight - eps;
  }
  static constexpr int scale = 2;
  template <bool use_look_ahead = true> pair<flow_t, cost_t> minCostMaxFlow() {
    cost_t retCost = 0;
    for (int i = 0; i < N; ++i)
      for (Edge &e : G[i])
        retCost += e.c * (e.f);
    // remove this for circulation
    flow_t retFlow = max_flow();
    h.assign(N, 0);
    ex.assign(N, 0);
    isq.assign(N, 0);
    cur.assign(N, 0);
    stack<int> q;
    for (; eps; eps >>= scale) {
      fill(cur.begin(), cur.end(), 0);
      for (int i = 0; i < N; ++i)
        for (auto &e : G[i])
          if (h[i] + e.c - h[e.to] < 0 && e.f)
            push(e, e.f);
      for (int i = 0; i < N; ++i) {
        if (ex[i] > 0) {
          q.push(i);
          isq[i] = 1;
        }
      }
      while (!q.empty()) {
        int u = q.top();
        q.pop();
        isq[u] = 0;
        while (ex[u] > 0) {
          if (cur[u] == G[u].size())
            relabel(u);
          for (int &i = cur[u], max_i = G[u].size(); i < max_i; ++i) {
            Edge &e = G[u][i];
            if (e.f == 0)
              continue;
            if (h[u] + e.c - h[e.to] < 0) {
              if (use_look_ahead && look_ahead(e.to)) {
                --i;
                continue;
              }
              push(e, ex[u]);
              if (isq[e.to] == 0) {
                q.push(e.to);
                isq[e.to] = 1;
              }
              if (ex[u] == 0)
                break;
            }
          }
        }
      }
      if (eps > 1 && eps >> scale == 0) {
        eps = 1 << scale;
      }
    }
    for (int i = 0; i < N; ++i) {
      for (Edge &e : G[i]) {
        retCost -= e.c * (e.f);
      }
    }
    return make_pair(retFlow, retCost / 2 / N);
  }
  flow_t getFlow(Edge const &e) { return G[e.to][e.rev].f; }
};

const int N = 50 + 7;
int n;
int m;
int a[N][N];
bool vis[N][N];
vector<int> reach[N][N];

int id(int r, int c) { return c + (r - 1) * m - 1; }

void gol() {
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      vis[i][j] = 0;
    }
  }
}

int cnt;
int r;
int c;

mcSFlow<int, int> f;

void dfs(int i, int j) {
  vis[i][j] = 1;
  f.add_edge(1 + id(r, c), 1 + n * m + id(i, j), 1, -(i + j - r - c));
  if (a[i + 1][j] != -1 && !vis[i + 1][j]) {
    dfs(i + 1, j);
  }
  if (a[i][j + 1] != -1 && !vis[i][j + 1]) {
    dfs(i, j + 1);
  }
}

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

  int sum = 0;
  cin >> n >> m;
  f.init(1 + n * m + n * m + 1, 0, 1 + n * m + n * m);
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      a[i][j] = -1;
    }
  }
  for (int i = 1; i <= n; i++) {
    string s;
    cin >> s;
    for (int j = 1; j <= m; j++) {
      if (s[j - 1] == '.') {
        a[i][j] = 0;
      }
      if (s[j - 1] == 'o') {
        a[i][j] = 1;
      }
      if (s[j - 1] == '#') {
        a[i][j] = -1;
      }
    }
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      if (a[i][j] == 1) {
        r = i;
        c = j;
        cnt++;
        sum = i + j;
        gol();
        dfs(i, j);
        f.add_edge(0, 1 + id(i, j), 1, 0);
      }
    }
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      f.add_edge(1 + n * m + id(i, j), 1 + n * m + n * m, 1, 0);
    }
  }
  pair<int, int> it = f.minCostMaxFlow();
  cout << -it.second << "\\n";

  /// cout << f.minCostMaxFlow(0, 1 + n * m + n * m).second << "\\n";
}


/*
{By GWj
*/
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define rb(a, b, c) for (int a = b; a <= c; ++a)
#define rl(a, b, c) for (int a = b; a >= c; --a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a, b) make_pair(a, b)
#define FIR first
#define SEC second
#define FREO freopen("check.out", "w", stdout)
#define rep(a, b) for (int a = 0; a < b; ++a)
#define SRAND                                                                  \\
  mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng() % a
#define ALL(a) a.begin(), a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin >> a
#define R2(a, b) cin >> a >> b
#define check_min(a, b) a = min(a, b)
#define check_max(a, b) a = max(a, b)
using namespace std;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> mp;
/*}
 */
const int GRAPH_SIZE = 50 * 50 + 100 + 20;
int pin[GRAPH_SIZE], dep[GRAPH_SIZE], s = 0, t = GRAPH_SIZE - 1;
struct EDGE {
  int u, v, c, cos;
};
vector<EDGE> e;
vector<int> each[GRAPH_SIZE];
int maxflow, mincost;
int flow[GRAPH_SIZE];
int dis[GRAPH_SIZE], inq[GRAPH_SIZE], las[GRAPH_SIZE];
bool spfa() {
  memset(inq, 0, sizeof(inq));
  memset(dis, 63, sizeof(dis));
  memset(flow, 63, sizeof(flow));
  dis[s] = 0;
  queue<int> q;
  q.push(s);
  inq[s] = 1;
  while (!q.empty()) {
    int now = q.front();
    q.pop();
    inq[now] = 0;
    for (auto it : each[now]) {
      int to, f, c;
      to = e[it].v;
      f = e[it].c;
      c = e[it].cos;
      if (f <= 0)
        continue;
      if (dis[to] > dis[now] + c) {
        dis[to] = dis[now] + c;
        las[to] = it;
        flow[to] = min(flow[now], f);
        if (!inq[to])
          q.push(to);
        inq[to] = 1;
      }
    }
  }
  return dis[t] != INF;
}
void KM() {
  while (spfa()) {
    maxflow += flow[t];
    mincost += dis[t] * flow[t];
    int now = t;
    while (now != s) {
      e[las[now]].c -= flow[t];
      e[las[now] ^ 1].c += flow[t];
      now = e[las[now]].u;
    }
  }
}
void make_edge(int U, int V, int C, int COS) {
  EDGE tmp;
  tmp.u = U;
  tmp.v = V;
  tmp.c = C;
  tmp.cos = COS;
  e.PB(tmp);
  each[U].PB(e.size() - 1);
  swap(tmp.u, tmp.v);
  tmp.c = 0;
  tmp.cos = -COS;
  e.PB(tmp);
  each[V].PB(e.size() - 1);
}
int n, m;
string maze[51];
bool vist[51][51];
int walk[2][2] = {{1, 0}, {0, 1}};
bool check(int x, int y) {
  if (x <= 0 || y <= 0 || x > n || y > m)
    return 0;
  if (maze[x][y] == '#')
    return 0;
  return 1;
}
void run(int x, int y) {
  vist[x][y] = 1;
  rep(i, 2) {
    int nx, ny;
    nx = x + walk[i][0], ny = y + walk[i][1];
    if (check(nx, ny)) {
      run(nx, ny);
    }
  }
}
int main() {
  fastio;
  R2(n, m);
  rb(i, 1, n) R(maze[i]);
  rb(i, 1, n) maze[i] = '^' + maze[i];
  int cnt = n * m;
  rb(i, 1, n) rb(j, 1, m) if (maze[i][j] == 'o') {
    memset(vist, 0, sizeof(vist));
    run(i, j);
    cnt++;
    rb(ii, 1, n) rb(jj, 1, m) if (vist[ii][jj]) {
      make_edge(cnt, ii * m - m + jj, 1, -(ii - i + jj - j));
    }
  }
  rb(i, n * m + 1, cnt) make_edge(s, i, 1, 0);
  rb(i, 1, n * m) make_edge(i, t, 1, 0);
  KM();
  cout << -mincost << endl;
  return 0;
}
/** 程序框架：
 *
 *
 *
 *
 **/

/*
{By GWj
*/
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define rb(a, b, c) for (int a = b; a <= c; ++a)
#define rl(a, b, c) for (int a = b; a >= c; --a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a, b) make_pair(a, b)
#define FIR first
#define SEC second
#define FREO freopen("check.out", "w", stdout)
#define rep(a, b) for (int a = 0; a < b; ++a)
#define SRAND                                                                  \\
  mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng() % a
#define ALL(a) a.begin(), a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin >> a
#define R2(a, b) cin >> a >> b
#define check_min(a, b) a = min(a, b)
#define check_max(a, b) a = max(a, b)
using namespace std;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> mp;
/*}
 */
const int GRAPH_SIZE = 50 * 50 + 100 + 20;
int pin[GRAPH_SIZE], dep[GRAPH_SIZE], s = 0, t = GRAPH_SIZE - 1;
struct EDGE {
  int u, v, c, cos;
};
vector<EDGE> e;
vector<int> each[GRAPH_SIZE];
int maxflow, mincost;
int flow[GRAPH_SIZE];
int dis[GRAPH_SIZE], inq[GRAPH_SIZE], las[GRAPH_SIZE];
bool spfa() {
  memset(inq, 0, sizeof(inq));
  memset(dis, 63, sizeof(dis));
  flow[s] = INF;
  dis[s] = 0;
  queue<int> q;
  q.push(s);
  inq[s] = 1;
  while (!q.empty()) {
    int now = q.front();
    q.pop();
    inq[now] = 0;
    for (auto it : each[now]) {
      int to, f, c;
      to = e[it].v;
      f = e[it].c;
      c = e[it].cos;
      if (f <= 0)
        continue;
      if (dis[to] > dis[now] + c) {
        dis[to] = dis[now] + c;
        las[to] = it;
        flow[to] = min(flow[now], f);
        if (!inq[to])
          q.push(to);
        inq[to] = 1;
      }
    }
  }
  return dis[t] != INF;
}
void KM() {
  while (spfa()) {
    maxflow += flow[t];
    mincost += dis[t] * flow[t];
    int now = t;
    while (now != s) {
      e[las[now]].c -= flow[t];
      e[las[now] ^ 1].c += flow[t];
      now = e[las[now]].u;
    }
  }
}
void make_edge(int U, int V, int C, int COS) {
  EDGE tmp;
  tmp.u = U;
  tmp.v = V;
  tmp.c = C;
  tmp.cos = COS;
  e.PB(tmp);
  each[U].PB(e.size() - 1);
  swap(tmp.u, tmp.v);
  tmp.c = 0;
  tmp.cos = -COS;
  e.PB(tmp);
  each[V].PB(e.size() - 1);
}
int n, m;
string maze[51];
int walk[2][2] = {{1, 0}, {0, 1}};
bool check(int x, int y) {
  if (x <= 0 || y <= 0 || x > n || y > m)
    return 0;
  if (maze[x][y] == '#')
    return 0;
  return 1;
}
int main() {
  fastio;
  R2(n, m);
  rb(i, 1, n) R(maze[i]);
  rb(i, 1, n) maze[i] = '^' + maze[i];
  rb(i, 1, n) rb(j, 1, m) {
    int id = (i - 1) * m + j;
    if (maze[i][j] == 'o')
      make_edge(s, id, 1, 0);
    make_edge(id, t, 1, 0);
    rep(k, 2) {
      int nx, ny;
      nx = i + walk[k][0];
      ny = j + walk[k][1];
      if (check(nx, ny)) {
        make_edge(id, (nx - 1) * m + ny, INF, -1);
      }
    }
  }
  KM();
  cout << -mincost << endl;
  return 0;
}
/** 程序框架：
 *
 *
 *
 *
 **/


/*
 * Author: Kewth

 * Date:
  echo -n '  ' && date +%Y.%m.%d # Type "!!sh" in Vim.

 * Solution:
  To be updated after "Accept".

 * Digression:

 * CopyRight:
   __  __                  __   __
  |  |/  |.-----.--.--.--.|  |_|  |--.
  |     < |  -__|  |  |  ||   _|     |
  |__|\\__||_____|________||____|__|__|

 */

#include <algorithm>
#include <cstdio>
#include <queue>
#define debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long ll;

static struct {
  inline operator int() {
    int x;
    return scanf("%d", &x), x;
  }
  inline operator ll() {
    ll x;
    return scanf("%lld", &x), x;
  }
  template <class T> inline void operator()(T &x) { x = *this; }
  template <class T, class... A> inline void operator()(T &x, A &...a) {
    x = *this;
    this->operator()(a...);
  }
} read;

const int Maxn = 55;
char str[Maxn][Maxn];
int id1[Maxn][Maxn], id2[Maxn][Maxn], ip;
bool mvis[Maxn][Maxn];

const int maxn = 2605, maxm = 252605;
int head[maxn], nxt[maxm << 1], to[maxm << 1], cap[maxm << 1], cost[maxm << 1],
    hp = 1;

inline void add(int u, int v, int ca, int co) {
  if ((hp & 1) and !ca)
    return;
  nxt[++hp] = head[u];
  head[u] = hp;
  to[hp] = v;
  cap[hp] = ca;
  cost[hp] = co;
  if (!(hp & 1))
    add(v, u, 0, -co);
}

ll dis[maxn];
bool inq[maxn], vis[maxn];
int from[maxn];
bool spfa(int s, int t) {
  std::queue<int> q;
  std::fill(dis, dis + t + 1, 1000000000000000000);
  std::fill(inq, inq + t + 1, 0);
  dis[s] = 0;
  q.push(s);
  while (!q.empty()) {
    int u = q.front();
    vis[u] = 1;
    q.pop();
    inq[u] = 0;
    for (int i = head[u]; i; i = nxt[i])
      if (cap[i] and dis[u] + cost[i] < dis[to[i]]) {
        dis[to[i]] = dis[u] + cost[i];
        from[to[i]] = i ^ 1;
        if (!inq[to[i]]) {
          q.push(to[i]);
          inq[to[i]] = 1;
        }
      }
  }
  return dis[t] < 1000000000000000000;
}

ll mincostmaxflow(int s, int t) {
  ll res = 0;
  while (spfa(s, t)) {
    int flow = cap[from[t] ^ 1];
    ll sum = cost[from[t] ^ 1];
    for (int u = to[from[t]]; u != s; u = to[from[u]]) {
      flow = std::min(flow, cap[from[u] ^ 1]);
      sum += cost[from[u] ^ 1];
    }
    cap[from[t] ^ 1] -= flow;
    cap[from[t]] += flow;
    for (int u = to[from[t]]; u != s; u = to[from[u]]) {
      cap[from[u] ^ 1] -= flow;
      cap[from[u]] += flow;
    }
    res += sum * flow;
  }
  return res;
}

void link(int x, int y, int i, int j) {
  if (str[i][j] == '#' or !str[i][j])
    return;
  mvis[i][j] = 1;
  add(id1[x][y], id2[i][j], 1, x + y - i - j);
  /* debug("%d %d %d %d\\n", x, y, i, j); */
  link(x, y, i + 1, j);
  link(x, y, i, j + 1);
}

int main() {
  int n = read, m = read;
  for (int i = 1; i <= n; i++)
    scanf("%s", str[i] + 1);
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++) {
      if (str[i][j] == 'o')
        id1[i][j] = ++ip;
      if (str[i][j] != '#')
        id2[i][j] = ++ip;
    }
  int S = ++ip, T = ++ip;
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++) {
      if (str[i][j] == 'o') {
        add(S, id1[i][j], 1, 0);
        link(i, j, i, j);
      }
      if (str[i][j] != '#')
        add(id2[i][j], T, 1, 0);
    }
  printf("%lld\\n", -mincostmaxflow(S, T));
}

/*
 * Author: Kewth

 * Date:
  echo -n '  ' && date +%Y.%m.%d # Type "!!sh" in Vim.

 * Solution:
  To be updated after "Accept".

 * Digression:

 * CopyRight:
   __  __                  __   __
  |  |/  |.-----.--.--.--.|  |_|  |--.
  |     < |  -__|  |  |  ||   _|     |
  |__|\\__||_____|________||____|__|__|

 */

#include <algorithm>
#include <cstdio>
#include <queue>
#define debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long ll;

static struct {
  inline operator int() {
    int x;
    return scanf("%d", &x), x;
  }
  inline operator ll() {
    ll x;
    return scanf("%lld", &x), x;
  }
  template <class T> inline void operator()(T &x) { x = *this; }
  template <class T, class... A> inline void operator()(T &x, A &...a) {
    x = *this;
    this->operator()(a...);
  }
} read;

const int Maxn = 55;
char str[Maxn][Maxn];
int id1[Maxn][Maxn], id2[Maxn][Maxn], ip;
bool mvis[Maxn][Maxn];

const int maxn = 2605, maxm = 252605;
int head[maxn], nxt[maxm << 1], to[maxm << 1], cap[maxm << 1], cost[maxm << 1],
    hp = 1;

inline void add(int u, int v, int ca, int co) {
  if ((hp & 1) and !ca)
    return;
  nxt[++hp] = head[u];
  head[u] = hp;
  to[hp] = v;
  cap[hp] = ca;
  cost[hp] = co;
  if (!(hp & 1))
    add(v, u, 0, -co);
}

ll dis[maxn];
bool inq[maxn], vis[maxn];
int from[maxn];
bool spfa(int s, int t) {
  std::queue<int> q;
  std::fill(dis, dis + t + 1, 1000000000000000000);
  std::fill(inq, inq + t + 1, 0);
  dis[s] = 0;
  q.push(s);
  while (!q.empty()) {
    int u = q.front();
    vis[u] = 1;
    q.pop();
    inq[u] = 0;
    for (int i = head[u]; i; i = nxt[i])
      if (cap[i] and dis[u] + cost[i] < dis[to[i]]) {
        dis[to[i]] = dis[u] + cost[i];
        from[to[i]] = i ^ 1;
        if (!inq[to[i]]) {
          q.push(to[i]);
          inq[to[i]] = 1;
        }
      }
  }
  return dis[t] < 1000000000000000000;
}

ll mincostmaxflow(int s, int t) {
  ll res = 0;
  while (spfa(s, t)) {
    int flow = cap[from[t] ^ 1];
    ll sum = cost[from[t] ^ 1];
    for (int u = to[from[t]]; u != s; u = to[from[u]]) {
      flow = std::min(flow, cap[from[u] ^ 1]);
      sum += cost[from[u] ^ 1];
    }
    cap[from[t] ^ 1] -= flow;
    cap[from[t]] += flow;
    for (int u = to[from[t]]; u != s; u = to[from[u]]) {
      cap[from[u] ^ 1] -= flow;
      cap[from[u]] += flow;
    }
    res += sum * flow;
  }
  return res;
}

void link(int x, int y, int i, int j) {
  if (mvis[i][j] or str[i][j] == '#' or !str[i][j])
    return;
  mvis[i][j] = 1;
  add(id1[x][y], id2[i][j], 1, x + y - i - j);
  /* debug("%d %d %d %d\\n", x, y, i, j); */
  link(x, y, i + 1, j);
  link(x, y, i, j + 1);
}

int main() {
  int n = read, m = read;
  for (int i = 1; i <= n; i++)
    scanf("%s", str[i] + 1);
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++) {
      if (str[i][j] == 'o')
        id1[i][j] = ++ip;
      if (str[i][j] != '#')
        id2[i][j] = ++ip;
    }
  int S = ++ip, T = ++ip;
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++) {
      if (str[i][j] == 'o') {
        add(S, id1[i][j], 1, 0);
        link(i, j, i, j);
        for (int ii = 1; ii <= n; ii++)
          for (int jj = 1; jj <= m; jj++)
            mvis[ii][jj] = 0;
      }
      if (str[i][j] != '#')
        add(id2[i][j], T, 1, 0);
    }
  printf("%lld\\n", -mincostmaxflow(S, T));
}


#include <iostream>
#include <queue>
#include <vector>
#define INF 0x3f3f3f3f

using namespace std;

struct Edge {
  int u, v, c, w;
  Edge(int u, int v, int c, int w) : u(u), v(v), c(c), w(w) {}
};
class Graph {
  int n, s, t;
  vector<int> flow, dist, pre, last;
  vector<bool> vis;
  vector<Edge> edges;
  vector<vector<int>> adj;

public:
  Graph(int n, int s, int t) : n(n), s(s), t(t) {
    adj = vector<vector<int>>(n);
    flow.reserve(n);
    dist.reserve(n);
    pre.reserve(n);
    last.reserve(n);
    vis.reserve(n);
  }
  void add_edge(int u, int v, int c, int w) {
    int m = edges.size();
    edges.emplace_back(u, v, c, w);
    edges.emplace_back(v, u, 0, -w);
    adj[u].emplace_back(m);
    adj[v].emplace_back(m | 1);
  }

  bool spfa() {
    dist.assign(n, INF);
    flow.assign(n, INF);
    vis.assign(n, false);
    queue<int> q;
    q.push(s);
    vis[s] = true;
    dist[s] = 0;
    pre[t] = -1;

    while (!q.empty()) {
      int u = q.front();
      q.pop();
      vis[u] = false;
      for (int e : adj[u]) {
        auto [_, v, c, w] = edges[e];
        if (c > 0 && dist[v] > dist[u] + w) {
          dist[v] = dist[u] + w;
          pre[v] = u;
          last[v] = e;
          flow[v] = min(flow[u], c);
          if (!vis[v]) {
            vis[v] = true;
            q.push(v);
          }
        }
      }
    }

    return pre[t] != -1;
  }

  pair<int, int> min_cost_max_flow() {
    int mincost = 0;
    int maxflow = 0;
    while (spfa()) {
      int now = t;
      mincost += flow[t] * dist[t];
      while (now != s) {
        edges[last[now]].c -= flow[t];
        edges[last[now] ^ 1].c += flow[t];
        now = pre[now];
      }
      maxflow += flow[t];
    }
    return {maxflow, mincost};
  }
};

using namespace std;
int main() {
  int n, m;
  cin >> n >> m;
  vector<string> s(n);
  for (int i = 0; i < n; ++i)
    cin >> s[i];
  int ball = 0;
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < m; ++j)
      if (s[i][j] == 'o')
        ball++;
  int nodes = ball + n * m + 2;
  Graph g(nodes, 0, nodes - 1);
  int hi = n + m;
  for (int i = 1; i <= ball; ++i)
    g.add_edge(0, i, 1, 0);
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < m; ++j) {
      int idx = ball + i * m + j + 1;
      g.add_edge(idx, nodes - 1, 1, 0);
    }
  int ball_idx = 1;
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < m; ++j) {
      if (s[i][j] != 'o')
        continue;
      vector<vector<int>> dp(n, vector<int>(m, INF));
      dp[i][j] = 0;
      for (int p = i; p < n; ++p)
        for (int q = j; q < n; ++q) {
          if (s[p][q] == '#')
            continue;
          if (p > i && dp[p - 1][q] != INF)
            dp[p][q] = dp[p - 1][q] + 1;
          if (q > j && dp[p][q - 1] != INF)
            dp[p][q] = dp[p][q - 1] + 1;
          if (dp[p][q] != INF) {
            int idx = ball + p * m + q + 1;
            g.add_edge(ball_idx, idx, 1, hi - dp[p][q]);
          }
        }
      ball_idx++;
    }
  auto [flow, cost] = g.min_cost_max_flow();
  int ans = hi * flow - cost;
  cout << ans;
}

#include <iostream>
#include <queue>
#include <vector>
#define INF 0x3f3f3f3f

using namespace std;

struct Edge {
  int u, v, c, w;
  Edge(int u, int v, int c, int w) : u(u), v(v), c(c), w(w) {}
};
class Graph {
  int n, s, t;
  vector<int> flow, dist, pre, last;
  vector<bool> vis;
  vector<Edge> edges;
  vector<vector<int>> adj;

public:
  Graph(int n, int s, int t) : n(n), s(s), t(t) {
    adj = vector<vector<int>>(n);
    flow.reserve(n);
    dist.reserve(n);
    pre.reserve(n);
    last.reserve(n);
    vis.reserve(n);
  }
  void add_edge(int u, int v, int c, int w) {
    int m = edges.size();
    edges.emplace_back(u, v, c, w);
    edges.emplace_back(v, u, 0, -w);
    adj[u].emplace_back(m);
    adj[v].emplace_back(m | 1);
  }

  bool spfa() {
    dist.assign(n, INF);
    flow.assign(n, INF);
    vis.assign(n, false);
    queue<int> q;
    q.push(s);
    vis[s] = true;
    dist[s] = 0;
    pre[t] = -1;

    while (!q.empty()) {
      int u = q.front();
      q.pop();
      vis[u] = false;
      for (int e : adj[u]) {
        auto [_, v, c, w] = edges[e];
        if (c > 0 && dist[v] > dist[u] + w) {
          dist[v] = dist[u] + w;
          pre[v] = u;
          last[v] = e;
          flow[v] = min(flow[u], c);
          if (!vis[v]) {
            vis[v] = true;
            q.push(v);
          }
        }
      }
    }

    return pre[t] != -1;
  }

  pair<int, int> min_cost_max_flow() {
    int mincost = 0;
    int maxflow = 0;
    while (spfa()) {
      int now = t;
      mincost += flow[t] * dist[t];
      while (now != s) {
        edges[last[now]].c -= flow[t];
        edges[last[now] ^ 1].c += flow[t];
        now = pre[now];
      }
      maxflow += flow[t];
    }
    return {maxflow, mincost};
  }
};

using namespace std;
int main() {
  int n, m;
  cin >> n >> m;
  vector<string> s(n);
  for (int i = 0; i < n; ++i)
    cin >> s[i];
  int ball = 0;
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < m; ++j)
      if (s[i][j] == 'o')
        ball++;
  int nodes = ball + n * m + 2;
  Graph g(nodes, 0, nodes - 1);
  int hi = n + m;
  for (int i = 1; i <= ball; ++i)
    g.add_edge(0, i, 1, 0);
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < m; ++j) {
      int idx = ball + i * m + j + 1;
      g.add_edge(idx, nodes - 1, 1, 0);
    }
  int ball_idx = 1;
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < m; ++j) {
      if (s[i][j] != 'o')
        continue;
      vector<vector<int>> dp(n, vector<int>(m, INF));
      dp[i][j] = 0;
      for (int p = i; p < n; ++p)
        for (int q = j; q < m; ++q) {
          if (s[p][q] == '#')
            continue;
          if (p > i && dp[p - 1][q] != INF)
            dp[p][q] = dp[p - 1][q] + 1;
          if (q > j && dp[p][q - 1] != INF)
            dp[p][q] = dp[p][q - 1] + 1;
          if (dp[p][q] != INF) {
            int idx = ball + p * m + q + 1;
            g.add_edge(ball_idx, idx, 1, hi - dp[p][q]);
          }
        }
      ball_idx++;
    }
  auto [flow, cost] = g.min_cost_max_flow();
  int ans = hi * flow - cost;
  cout << ans;
}