#include <iostream>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
//#include <bits/stdc++.h>

using ll = long long;


void print(){ std::cout << std::endl; }
template <typename Head> void print(Head&& head) { std::cout << head << std::endl; }
template <typename Head, typename... Tail> void print(Head&& head, Tail&&... tail)
{
  std::cout << head << " ";
  print(std::forward<Tail>(tail)...); // or print(tail...);
}


const ll INF = (ll)1e18;
const ll MOD = 1e9 + 7;

ll H, W, V;
ll sy, sx, gy, gx;
ll dy[4] = { 0,  0, -1, +1};
ll dx[4] = {-1, +1,  0,  0};

ll pos2idx(ll y, ll x) { return y * W + x; }
ll idx2y(ll idx) { return idx / W; }
ll idx2x(ll idx) { return idx % W; }

typedef struct {
  ll from;
  ll to;
  ll cost;
} S_Edge;

struct UnionFind {
  std::vector<ll> par;
  std::vector<ll> rank;
  std::vector<ll> b_size;
  std::vector<ll> w_size;
  std::vector<ll> diff_weight;

  UnionFind(std::string& idx_grid) {
    ll N = (ll)idx_grid.size();
    par         = std::vector<ll>(N, 0LL);
    rank        = std::vector<ll>(N, 0LL);
    b_size      = std::vector<ll>(N, 0LL);
    w_size      = std::vector<ll>(N, 0LL);
    diff_weight = std::vector<ll>(N, 0LL);
    for (ll i = 0; i < N; i++) {
      par[i] = i;
      if (idx_grid[i] == '#') b_size[i] = 1LL;
      if (idx_grid[i] == '.') w_size[i] = 1LL;
    }
  }

  ll find (ll x) {
    if (par[x] == x) return x;
    ll root = find(par[x]);
    diff_weight[x] += diff_weight[par[x]];
    par[x] = root;
    return root;
  }

  void unite(ll x, ll y, ll w) {
    w += get_weight(x);
    w -= get_weight(y);

    x = find(x);
    y = find(y);
    if (x == y) return;

    b_size[x] += b_size[y];
    b_size[y] = b_size[x];
    w_size[x] += w_size[y];
    w_size[y] = w_size[x];

    if (rank[x] < rank[y]) std::swap(x, y), w = -w;
    if (rank[x] == rank[y]) rank[x]++;

    par[y] = x;
    diff_weight[y] = w;
    //print(x, y, diff_weight[x], diff_weight[y]);

    return;
  }

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

  ll get_b_size(ll x){
    return b_size[find(x)];
  }

  ll get_w_size(ll x){
    return w_size[find(x)];
  }

  ll get_weight(ll x){
    find(x);
    return diff_weight[x];
  }

  ll get_diff(ll x, ll y){
    //print("diff", y, x, get_weight(y), get_weight(x));
    return get_weight(y) - get_weight(x);
  }
};

void research_grid(std::vector< std::string >& grid, UnionFind& uf)
{
  for (ll y = 0; y < H; y++) {
    for (ll x = 0; x < W; x++) {
      for (ll d = 0; d < 4; d++) {
	ll ny = y + dy[d];
	ll nx = x + dx[d];
	ll from = pos2idx( y,  x);
	ll to   = pos2idx(ny, nx);
	if (ny >= 0 && nx >= 0 && ny < H && nx < W) {
	  if (grid[y][x] != grid[ny][nx]) {
	    uf.unite(from, to, 0LL);
	  }
	}
      }
    }
  }
}

int main()
{
  std::cin >> H >> W;
  V = H * W;

  std::vector< std::string > grid(H, std::string(W, 'x'));
  std::string idx_grid;

  for (ll y = 0; y < H; y++) {
    std::cin >> grid[y];
    idx_grid += grid[y];
  }

  UnionFind uf(idx_grid);
  research_grid(grid, uf);

  std::vector<bool> vused(V, false);

  ll ans = 0LL;
  for (ll v = 0; v < V; v++) {
    if (vused[uf.find(v)]) continue;

    ll b_size = uf.get_b_size(v);
    ll w_size = uf.get_w_size(v);
    vused[uf.find(v)] = true;
    ans += b_size * w_size;
  }

  print(ans);

  return 0;
}


Measured memory_bytes: 7040