#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iomanip>

using namespace std;

#define PB push_back
#define ALL(v) (v).begin(), (v).end()
#define SZ size()
#define FOR(i, a, b) for(int (i) = (a); (i) < (b); (i)++)
#define REP(i, n) FOR((i), 0, (n))
#define GET(a) cin >> (a)
#define GET2(a, b) cin >> (a) >> (b)
#define GET3(a, b, c) cin >> (a) >> (b) >> (c)
#define SHOW(v) cout << (v) << endl
#define SHOW2(a, b) cout << (a) << " " << (b) << endl
#define SHOW3(a, b, c) cout << (a) << " " << (b) << " " << (c) << endl
#define MOD 1000000007
template<class T> void GETV(T &v) { REP(i, v.size()) { GET(v[i]); } }
template<class T> void SHOWV(T &v) { REP(i, v.size()) { SHOW(v[i]); } }

typedef long long ll;

struct dim2 {
	int x;
	int y;
};

int main()
{
	int h, w;
	cin >> h >> w;
	
	vector<string> map(h);
	GETV(map);
	
	ll ans = 0;
	bool flag[h][w];
	for(int k = 0; k < h; k++) {
		for(int l = 0; l < w; l++) {
			flag[k][l] = false;
		}
	}
	for(int i = 0; i < h; i++) {
		for(int j = 0; j < w; j++) {
			vector<dim2> queue;
			if(!flag[i][j]) {
				dim2 tmp;
				tmp.x = i, tmp.y = j;
				queue.PB(tmp);		
				flag[i][j] = true;
			}
			
			ll num1 = 0;
			ll num2 = 0;
			if(map[i][j] == '.') {
				num2++;
			}
			else {
				num1++;
			}
			while(!queue.empty()) {
				if(map[queue[0].x][queue[0].y] == '.') {
					if(queue[0].x + 1 < h && flag[queue[0].x + 1][queue[0].y] == false && map[queue[0].x + 1][queue[0].y] == '#') {
						dim2 ttmp;
						ttmp.x = queue[0].x + 1, ttmp.y = queue[0].y;
						queue.PB(ttmp);
						flag[queue[0].x + 1][queue[0].y] = true;
						num1++;
					}
					if(queue[0].x - 1 > -1 && flag[queue[0].x - 1][queue[0].y] == false && map[queue[0].x - 1][queue[0].y] == '#') {
						dim2 ttmp;
						ttmp.x = queue[0].x - 1, ttmp.y = queue[0].y;
						queue.PB(ttmp);
						flag[queue[0].x - 1][queue[0].y] = true;
						num1++;
					}
					if(queue[0].y + 1 < w && flag[queue[0].x][queue[0].y + 1] == false && map[queue[0].x][queue[0].y + 1] == '#') {
						dim2 ttmp;
						ttmp.x = queue[0].x, ttmp.y = queue[0].y + 1;
						queue.PB(ttmp);
						flag[queue[0].x][queue[0].y + 1] = true;
						num1++;
					}
					if(queue[0].y - 1 > -1 && flag[queue[0].x][queue[0].y - 1] == false && map[queue[0].x][queue[0].y - 1] == '#') {
						dim2 ttmp;
						ttmp.x = queue[0].x, ttmp.y = queue[0].y - 1;
						queue.PB(ttmp);
						flag[queue[0].x][queue[0].y - 1] = true;
						num1++;
					}
				}
				else {
					if(queue[0].x + 1 < h && flag[queue[0].x + 1][queue[0].y] == false && map[queue[0].x + 1][queue[0].y] == '.') {
						dim2 ttmp;
						ttmp.x = queue[0].x + 1, ttmp.y = queue[0].y;
						queue.PB(ttmp);
						flag[queue[0].x + 1][queue[0].y] = true;
						num2++;
					}
					if(queue[0].x - 1 > -1 && flag[queue[0].x - 1][queue[0].y] == false && map[queue[0].x - 1][queue[0].y] == '.') {
						dim2 ttmp;
						ttmp.x = queue[0].x - 1, ttmp.y = queue[0].y;
						queue.PB(ttmp);
						flag[queue[0].x - 1][queue[0].y] = true;
						num2++;
					}
					if(queue[0].y + 1 < w && flag[queue[0].x][queue[0].y + 1] == false && map[queue[0].x][queue[0].y + 1] == '.') {
						dim2 ttmp;
						ttmp.x = queue[0].x, ttmp.y = queue[0].y + 1;
						queue.PB(ttmp);
						flag[queue[0].x][queue[0].y + 1] = true;
						num2++;
					}
					if(queue[0].y - 1 > -1 && flag[queue[0].x][queue[0].y - 1] == false && map[queue[0].x][queue[0].y - 1] == '.') {
						dim2 ttmp;
						ttmp.x = queue[0].x, ttmp.y = queue[0].y - 1;
						queue.PB(ttmp);
						flag[queue[0].x][queue[0].y - 1] = true;
						num2++;
					}
				}
				queue[0] = queue[queue.size() - 1];
				queue.pop_back();
			}
			
			ans += num1 * num2;
		}
	}
	
	cout << ans << endl;
	
    return 0;
}

Measured memory_bytes: 1784