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

const int N = 100;

char s[N][N];

int dx[4] = {0, -1, 0, 1};
int dy[4] = {1, 0, -1, 0};

int solve() {
  int n, m;
  scanf("%d %d", &n, &m);

  for (int i = 0; i < n; ++i) {
    scanf("%s", s[i]);
  }

  int ans = 0;
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < m; ++j) {
      int cnt = 0;
      int orient = 0;
      for (int k = 0; k < 4; ++k) {
        int nx = i + dx[k], ny = j + dy[k];

        if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
        if (s[i][j] != s[nx][ny]) {
          orient |= 1 << (k & 1);
          ++cnt;
        }
      }

      if (cnt == 2 && orient == 3) ++ans;
      else if (cnt == 3) ans += 2;
      else if (cnt == 4) ans += 4;
    }
  }

  printf("%d\n", ans);
  return 0;
}

int main() {
  int t = 1;
  // scanf("%d", &t);
  for (int tc = 0; tc < t; ++tc) {
    solve();
  }
  return 0;
}

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

//UnionFindテンプレ
class UnionFind {
    // まとめる 判定 サイズを知る
    public:
    // Aから見た親の番号を格納する。rootだったら-1*その集合のサイズ。
    vector<int> Parent;

    // 初期化
    UnionFind(int N) {
        Parent = vector<int>(N, -1);
    }

    // Aのrootを調べる
    int root(int A) {
        if (Parent[A] < 0) return A;  // マイナスならそれはroot
        return Parent[A] = root(Parent[A]);
    }

    // rootの値をプラスに戻して返す(サイズ)
    int size(int A) {
        return -Parent[root(A)];
    }

    // くっつける関数
    bool connect(int A, int B) {
        // AとBのroot同士をくっつける
        A = root(A);  // ここのAは、"rootの場所"の意味
        B = root(B);
        if (A == B) return false;  // 既にくっついている
        if (size(A) < size(B))
        swap(A, B);            // 大きい方にくっつけるために中身交換
        Parent[A] += Parent[B];  // 中身更新
        Parent[B] = A;
        return true;
    }

    //連結か調べる関数
    bool issame(int A, int B) {
        return root(A) == root(B);
    }
};

//chmin,chmax関数(DP用)
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;
}

//aのZ/mZでの逆元
int64_t modinv(int64_t a, int64_t m) {
    int64_t b = m, u = 1, v = 0;
    while (b) {
        int64_t t = a / b;
        a -= t * b; swap(a, b);
        u -= t * v; swap(u, v);
    }
    u %= m;
    if (u < 0) u += m;
    return u;
}



//最大公約数
int64_t gcd(int64_t a, int64_t b) {
    if (a % b == 0) {
        return b;
    }
    return gcd(b, a%b);
}
int64_t lcm(int64_t a, int64_t b) {
    return a*b/gcd(a,b);
}
//エラストテレスのふるい
vector<bool> make_is_prime(int N) {
    vector<bool> prime(N + 1, true);
    if (N >= 0) prime[0] = false;
    if (N >= 1) prime[1] = false;
    for (int i = 2; i * i <= N; i++) {
        if (!prime[i]) continue;
        for (int j = i * i; j <= N; j += i) {
            prime[j] = false;
        }
    }
    return prime;
}
//累乗a^b
int64_t pow(int a, int b) {
    int64_t ans = (int64_t)1;
    for (int i = 0; i < b; i++) {
        ans *= (int64_t)a;
    }
    return ans;
}

//ここから

int main() {
    int H,W; cin>>H>>W;
    vector<vector<char>> map(H,vector<char>(W));
    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W; j++) {
            cin>>map.at(i).at(j);
        }
    }
    int ans = 0;
    for (int i = 0; i < H-1; i++) {
        for (int j = 0; j < W-1; j++) {
            int cnt = 0;
            if (map.at(i).at(j) == '#') cnt++;
            if (map.at(i+1).at(j) == '#') cnt++;
            if (map.at(i).at(j+1) == '#') cnt++;
            if (map.at(i+1).at(j+1) == '#') cnt++;
            if (cnt == 1 || cnt == 3) ans++;
        }
    }
    cout << ans << endl;
}

