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

#define loop(i, a, b) for (int i = (a); i < (b); i++)
const int MAX = 405;
char grid[MAX][MAX];
int  visited[MAX][MAX];
int h, w;

bool inside(int x, int y)
{
    return 0 <= x && x < h && 0 <= y && y < w;
}

bool check(int x, int y, int a, int b){
    if (!inside(x + a, y + b))
        return false;
    return grid[x][y] != grid[x + a][y + b] && !visited[x + a][y + b];
}

int main()
{
    cin >> h >> w;
    loop(i, 0, h) loop(j, 0, w)
    {
        cin >> grid[i][j];
    }
    long long ans = 0;
    loop(i, 0, h) loop(j, 0, w) {
        if (visited[i][j]) continue;
        queue<pair<int, int>> queue;
        queue.emplace(make_pair(i, j));
        visited[i][j] = 1;
        long long black = 0;
        long long white = 0;
        while(queue.size()){
            pair<int, int> p = queue.front();
            queue.pop();
            if (grid[p.first][p.second] == '#')
                black += 1;
            else 
                white += 1;
            for (int a: {-1, 0, 1}){
                for (int b: {-1, 0, 1}){
                    if(abs(a) == abs(b)) continue;
                    if(check(p.first, p.second, a, b)){
                        queue.emplace(make_pair(p.first + a, p.second + b));
                        visited[p.first + a][p.second + b] = 1;
                    }
                }
            }
        }
        ans += white * black;
    }
    cout << ans << "\n";
    return 0;
}
// https://atcoder.jp/contests/aising2019/tasks/aising2019_c
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using vi = vector<int>;
using vl = vector<ll>;
#define rep(i, n) for(ll i = 0;i < n;i++)
#define all(i) i.begin(), i.end()
template<class T, class U> bool cmax(T& a, U b) { if (a<b) {a = b; return true;} else return false; }
template<class T, class U> bool cmin(T& a, U b) { if (a>b) {a = b; return true;} else return false; }

struct UnionFind {
  struct Node {
    ll parent;
    ll size;
  };
  vector<Node> tree;

  UnionFind(ll size) : tree(size) {
    for (ll i = 0; i < size; i++) {
      tree[i].parent = i;
      tree[i].size = 1;
    }
  }

  ll find(ll x) {
    while (tree[x].parent != x) {
      ll p = tree[x].parent;
      tree[x].parent = tree[p].parent;
      x = p;
    }
    return x;
  }

  void unite(ll x, ll y) {
    x = find(x);
    y = find(y);
    if (x == y) return;

    if (tree[x].size < tree[y].size) swap(x, y);
    tree[y].parent = x;
    tree[x].size += tree[y].size;
  }

  bool same(ll x, ll y) {
    return find(x) == find(y);
  }
};

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

  int h, w;
  cin >> h >> w;
  vector<string> s(h);
  rep(i, h) cin >> s[i];

  vi dx = {0, 0, 1, -1};
  vi dy = {1, -1, 0, 0};
  UnionFind uf(h*w);
  rep(i, h) rep(j, w) rep(k, 4) {
    int ni = i+dx[k];
    int nj = j+dy[k];
    if (0 <= ni && ni < h && 0 <= nj && nj < w && s[i][j] != s[ni][nj]) {
      uf.unite(i*w + j, ni*w + nj);
    }
  }

  vector<ll> black(h*w, 0), white(h*w, 0);
  rep(i, h) rep(j, w) {
    if (s[i][j] == '#') black[uf.find(i*w + j)]++;
    else white[uf.find(i*w + j)]++;
  }

  ll ans = 0;
  rep(i, h*w) {
    ans += black[i] * white[i];
  }
  cout << ans << endl;
}
