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

int	get_g(int i, vector<int> &grp) {
	if (i==grp.at(i)) return i;
	return get_g(grp.at(i), grp);
}

int     main() {
	int H, W;
	cin >>H >>W;
	vector<vector<int>> S(H+2, vector<int> (W+2, -1));
	for (int i=1; i<=H; i++)
		for (int j=1; j<=W; j++) {
			char c;
			cin >>c;
			if ('#'==c)
				S.at(i).at(j)=1;
			else
				S.at(i).at(j)=0;
		}
	// N: Number of nodes
	int N=H*W;
//	// M: Number of links
//	int M;
//	cin >>N >>M;
	// Data store
//	// p: Original value for each position
	// grp: Group#
	// grp_depth: Steps to the group root
//	vector<int> p(N+1, 0);
	vector<int> grp(N+1, 0);
	vector<int> grp_depth(N+1, 0);
//	// Read data for each node
	// Initialize group# as each own
	for (int i=1; i<=N; i++) {
//		cin >>p.at(i);
		grp.at(i)=i;
	}
	// Links
//	// Each link connects nodes x and y
//	// (Swap rules)
//	vector<int> x(M, 0);
//	vector<int> y(M, 0);
//	for (int i=0; i<M; i++)
//		cin >>x.at(i) >>y.at(i);
	// Grouping
	for (int i=1; i<=H; i++) {
		for (int j=1; j<=W; j++) {
			int I=(i-1)*W+j;
			int xx=get_g(I, grp);
			if ((j<W)&&(1==S.at(i).at(j)+S.at(i).at(j+1))) {
				int J=I+1;
				int yy=get_g(J, grp);
				if (xx!=yy) {
					if (grp_depth.at(xx)==grp_depth.at(yy)) {
						// Groups have same height
						// Merge into less-number-group
						grp.at(max(xx, yy)) = get_g(min(xx, yy), grp);
						grp_depth.at(min(xx, yy))++;
					} else if (grp_depth.at(xx)>grp_depth.at(yy)) {
						// x is higher
						// Merge into x so that the hight will not increase
						// (height for y will be +1)
						grp.at(yy) = xx;
					} else
						// y is higher
						// Merget into y
						grp.at(xx) = yy;
				}
			}
			if ((i<H)&&(1==S.at(i).at(j)+S.at(i+1).at(j))) {
				int J=I+W;
				int yy=get_g(J, grp);
				if (xx!=yy) {
					if (grp_depth.at(xx)==grp_depth.at(yy)) {
						// Groups have same height
						// Merge into less-number-group
						grp.at(max(xx, yy)) = get_g(min(xx, yy), grp);
						grp_depth.at(min(xx, yy))++;
					} else if (grp_depth.at(xx)>grp_depth.at(yy)) {
						// x is higher
						// Merge into x so that the hight will not increase
						// (height for y will be +1)
						grp.at(yy) = xx;
					} else
						// y is higher
						// Merget into y
						grp.at(xx) = yy;
				}
			}
		}
	}
/*
	for (int i=0; i<M; i++) {
		int xx=x.at(i);
		int yy=y.at(i);
		xx=get_g(xx, grp);
		yy=get_g(yy, grp);
		// Already in same group, do nothing
		if (xx==yy) continue;
		// Put into one group
		if (grp_depth.at(xx)==grp_depth.at(yy)) {
			// Groups have same height
			// Merge into less-number-group
			grp.at(max(xx, yy)) = get_g(min(xx, yy), grp);
			grp_depth.at(min(xx, yy))++;
		} else if (grp_depth.at(xx)>grp_depth.at(yy)) {
			// x is higher
			// Merge into x so that the hight will not increase
			// (height for y will be +1)
			grp.at(yy) = xx;
		} else
			// y is higher
			// Merget into y
			grp.at(xx) = yy;
	}
*/
/*
	int S=0;
	// By swapping, how many can be poisitioned as its value==position
	for (int i=1; i<=N; i++) {
		if (i==p.at(i))
			// Value==Position, already as expected
			S++;
		else if (get_g(i, grp)==get_g(p.at(i), grp))
			// In the same group, can be re-poisitioned
			S++;
	}
	cout <<S <<endl;
*/
	vector<vector<int>> GG(0);
	for (int i=1; i<=H; i++)
		for (int j=1; j<=W; j++)
			GG.push_back({get_g((i-1)*W+j, grp), S.at(i).at(j)});
	sort(GG.begin(), GG.end());
	long SS=0;
	long w=0;
	long b=0;
	int g=-1;
	for (int i=0; i<GG.size(); i++) {
		if (g!=GG.at(i).at(0)) {
			SS +=b*w;
			g=GG.at(i).at(0);
			w=0;
			b=0;
		}
		if (0==GG.at(i).at(1))
			w++;
		else
			b++;
	}
	SS +=b*w;
	cout <<SS <<endl;
	return 0;
};


Measured memory_bytes: 14128