#include <bits/stdc++.h>
using namespace std;
#define rep(i,N) for(int i=0,i##_max=(N);i<i##_max;++i)
#define repp(i,l,r) for(int i=(l),i##_max=(r);i<i##_max;++i)
#define per(i,N) for(int i=(N)-1;i>=0;--i)
#define perr(i,l,r) for(int i=r-1,i##_min(l);i>=i##_min;--i)
#define all(arr) (arr).begin(), (arr).end()
#define SP << " " <<
#define SPF << " "
#define SPEEDUP cin.tie(0);ios::sync_with_stdio(false);
#define MAX_I INT_MAX //1e9
#define MIN_I INT_MIN //-1e9
#define MAX_UI UINT_MAX //1e9
#define MAX_LL LLONG_MAX //1e18
#define MIN_LL LLONG_MIN //-1e18
#define MAX_ULL ULLONG_MAX //1e19
  typedef long long ll;
  typedef pair<int,int> PII;
  typedef pair<char,char> PCC;
  typedef pair<ll,ll> PLL;
  typedef pair<char,int> PCI;
  typedef pair<int,char> PIC;
  typedef pair<ll,int> PLI;
  typedef pair<int,ll> PIL; 
  typedef pair<ll,char> PLC; 
  typedef pair<char,ll> PCL; 

inline void YesNo(bool b){ cout << (b?"Yes" : "No") << endl;}
inline void YESNO(bool b){ cout << (b?"YES" : "NO") << endl;}
inline void Yay(bool b){ cout << (b?"Yay!" : ":(") << endl;}

class UnionFind{
  private:
    vector<int> Parent;
  public:
    vector<ll> White;
    vector<ll> Black;

    UnionFind(int N){
      Parent = vector<int>(N,-1);
      White = vector<ll>(N,0);
      Black = vector<ll>(N,0);
    }

    int find(int x){
      if(Parent[x] < 0) return x;
      return Parent[x] = find(Parent[x]);
    }

    int size(int x){
      return -Parent[find(x)];
    }

    bool unite(int x, int y){
      x = find(x);
      y = find(y);
      if(x==y) return false;

      if(size(x)<size(y))swap(x,y);
      Parent[x] += Parent[y];
      White[x] += White[y];
      Black[x] += Black[y];
      Parent[y] = x;
      White[y] = 0;
      Black[y] = 0;
      return true;
    }

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

const int HMAX = 410, WMAX = 410;
int H,W;
vector<vector<char> > maze(WMAX,vector<char>(HMAX));
vector<vector<bool> > reach(WMAX,vector<bool>(HMAX,false));

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

bool isArea(int x,int y){
  return x>=0 && x<W && y>=0 && y<H;
}

ll solve(){
  queue<PII> que;
  UnionFind uni = UnionFind(W*H);
  rep(y,H) rep(x,W){
    if(maze[x][y] == '#') uni.Black[y*W+x] = 1;
    else uni.White[y*W+x] = 1;
  }

  rep(y,H) rep(x,W){
    if(uni.size(y*W+x) != 1)continue;
    que.push(PII(x,y));
    while(!que.empty()){
      int x = que.front().first;
      int y = que.front().second;
      que.pop();
      if(reach[x][y])continue;
      reach[x][y] = true;
      rep(i,4){
        int nx = x + dx[i], ny = y+dy[i];
        if(!isArea(nx,ny))continue;
        if(maze[x][y] == maze[nx][ny])continue;
        que.push(PII(nx,ny));
        uni.unite(ny*W+nx, y*W+x);
      }
    }
  }
  reach = vector<vector<bool> > (WMAX, vector<bool> (HMAX,false));
  ll ans = 0;
  rep(y,H) rep(x,W){
    int n = uni.find(y*W+x);
    int x_ = n%W, y_ = n/W;
    if(reach[x_][y_]) continue;
    reach[x_][y_] = true;
    ans += uni.Black[n]*uni.White[n];
  }
  return ans;
}

int main(void){
  SPEEDUP
  cout << setprecision(15);
  cin >> H >> W;
  rep(y,H) rep(x,W)cin >> maze[x][y];
  cout << solve() << endl;
  return 0;
}



Measured memory_bytes: 3712