#include<iostream>
#include<vector>
#include<string>
#include<math.h>
#include<algorithm>
#include<numeric>
#include<list>
#include<stack>

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef vector<LL> VLL;
typedef vector<ULL> VULL;

typedef signed short Hitoketa;

class MYCP {
public:

	static const LL TEISUU = 1000 * 1000 * 1000 + 7;

	static LL DebugFlag;

	//数値を区切って文字列にする
	static string MakeString_LongLong(vector<long long> const& numbers, string const& str) {
		if (numbers.size() == 0)return "";
		string result = "" + to_string(numbers[0]);
		for (long long i = 1; i < numbers.size(); i++) {
			result += str;
			result += to_string(numbers[i]);
		}

		return result;
	}

	//空白で区切る為のオーバーロード
	static string MakeString_LongLong(vector<long long> const& numbers) {
		if (numbers.size() == 0)return "";
		string result = "" + to_string(numbers[0]);
		for (long long i = 1; i < numbers.size(); i++) {
			result += " ";
			result += to_string(numbers[i]);
		}

		return result;
	}



	//文字列の配列を改行を挟んでまとめる
	static string MakeString_VectorString(vector<string> const& str) {
		string result = "";
		for (long long i = 0; i < str.size(); i++) {
			result += str[i] + "\n";
		}

		return result;
	}

	//文字列を必要な個数だけ読み取る
	static vector<string> MyReadLineSplit(LL n) {
		vector<string> str(n);
		for (long long i = 0; i < n; i++) {
			std::cin >> str[i];
		}

		return str;
	}

	//数値を必要な個数だけ読み取る
	static vector<long long> ReadInts(long long number) {
		vector<long long> a(number);
		for (int i = 0; i < number; i++) {
			std::cin >> a[i];
		}

		return a;
	}

	//渡された自然数が素数ならtureを返す
	static bool PrimeCheck_Int(long long number) {
		if (number < 2)return false;

		for (ULL i = 2; i*i <= number; i++) {
			if (number%i == 0)return false;
		}

		return true;
	}

	//渡された数値以下の素数表を作る
	static vector<long long> MakePrimeList(long long n) {
		vector<long long> list;
		LL i, j, p;
		bool flag;
		for (i = 2; i <= n; i++) {
			flag = true;
			for (j = 0; j < list.size(); j++) {

				if (!(list[j] * list[j] <= i))break;

				if (i%list[j] == 0) {
					flag = false;
					break;
				}
			}
			if (flag)list.push_back(i);
		}

		return list;
	}



	//文字列の分割
	static vector<string> split(string const& str, char sep)
	{
		vector<std::string> v;        // 分割結果を格納するベクター
		auto first = str.begin();              // テキストの最初を指すイテレータ
		while (first != str.end()) {         // テキストが残っている間ループ
			auto last = first;                      // 分割文字列末尾へのイテレータ
			while (last != str.end() && *last != sep)       // 末尾 or セパレータ文字まで進める
				last++;
			v.push_back(string(first, last));       // 分割文字を出力
			if (last != str.end())
				last++;
			first = last;          // 次の処理のためにイテレータを設定
		}
		return v;
	}

	//合計を求める
	static LL Sum(vector<LL> a) {
		LL i, sum = 0;

		for (i = 0; i < a.size(); i++) {
			sum += a[i];
		}

		return sum;
	}

	//小文字ならtrueを返す
	static bool Komoji(char a) {
		if (a >= 'a'&&a <= 'z')return true;

		return false;
	}

	//大文字ならtrueを返す
	static bool Oomoji(char a) {
		if (a >= 'A'&&a <= 'Z')return true;

		return false;
	}

	static LL GreatestCommonFactor(LL a, LL b) {
		LL temp;
		if (a < b) {
			temp = b;
			b = a;
			a = temp;
		}

		while (true)
		{
			temp = a % b;
			a = b;
			b = temp;
			if (b == 0)break;
		}

		return a;
	}

	static LL LeastCommonMultiple(LL a, LL b) {
		return (a / GreatestCommonFactor(a, b))*b;
	}

	static LL DebugPrintf(string output) {

		if (MYCP::DebugFlag != 0) {
			std::cout << output << endl;
		}

		return MYCP::DebugFlag;
	}

	static  LL DebugCin() {
		LL a;

		if (MYCP::DebugFlag != 0) {
			cin >> a;
		}

		return a;
	}
};
LL MYCP::DebugFlag = 0;

//累積和を求めるクラス
class Syakutori {
private:
	vector<LL> list;

public:

	void MakeArray(vector<LL> data) {
		LL i;
		list = data;
		list.push_back(0);
		list[0] = 0;
		for (i = 1; i < list.size(); i++) {
			list[i] = list[i - 1] + data[i - 1];
		}

	}

	LL Sum(LL start, LL end) {
		if (end < start) {
			std::cout << "startがendより大きいです";
			return 0;
		}
		if (start < 0 || end >= list.size()) {
			std::cout << "範囲が異常";
			return 0;
		}

		return list[end] - list[start];
	}

};

class Unlimited_Integer {
private:
	Hitoketa sign;
	vector<Hitoketa> numbers;
	

public:

	Unlimited_Integer(string value) {
		LL i, j, k;

		this->sign = 1;
		k = 0;

		if (value[0] == '-') {
			sign = -1;
			k = 1;
		}

		vector<Hitoketa> temp(value.size() - k, 100);

		this->numbers = temp;

		for (i = value.size() - 1, j = 0; i >= k; i--, j++) {
			this->numbers[j] = (Hitoketa)(value[i] - '0');
		}

	}

	Unlimited_Integer(LL value) {
		Unlimited_Integer temp(to_string(value));
		this->Copy(temp);
	}

	Unlimited_Integer() {
		Unlimited_Integer temp(0);
		this->Copy(temp);
	}

	string To_string() {
		LL i, j, k;

		string result = "";

		if (this->sign == -1) {
			result += "-";
		}

		for (i = this->numbers.size() - 1; i >= 0; i--) {
			result += (char)(this->numbers[i] + '0');
		}

		return result;
	}

	void Copy(Unlimited_Integer a) {
		this->numbers = a.numbers;
		this->sign = a.sign;
	}

	void operator =(Unlimited_Integer const& a) {
		this->Copy(a);
	}

	Unlimited_Integer operator +(Unlimited_Integer const& a) {
		Unlimited_Integer result;

		result = *this;

		//ここから
	}
};

char reverse(char a) {
	if (a == '#')return '.';
	return '#';
}

//ここからメイン
int main(void) {
	MYCP::DebugFlag = 0;
	LL i, j, k, n, m, h, w, c1, c2, s, t, x, y;

	LL result = 0;

	char black = '#', white = reverse(black);

	cin >> h >> w;

	vector<string> field(h);

	vector<VLL> record;

	VLL vec1{ 1,0,-1,0 }, vec2{ 0,1,0, -1 };

	for (i = 0; i < h; i++) {
		cin >> field[i];

		VLL temp(w, 0);
		record.push_back(temp);
	}

	stack<LL> stack1, stack2;

	k = 0;

	while (true)
	{
		c1 = 0;
		c2 = 0;

		//このあたり
		for (; k < h*w; k++) {

			s = k / w;
			t = k % w;
			if(true){

				if (field[s][t] == black) {
					if (record[s][t] == 0) {
						stack1.push(s);
						stack2.push(t);
						record[s][t] = 1;
						if (field[s][t] == black)c1++;
						else c2++;

						goto ravel;
					}
				}
			}
		}

		break;

	ravel:
		i = 0;

		

		while (stack1.size() > 0)
		{
			n = stack1.top();
			stack1.pop();

			m = stack2.top();
			stack2.pop();


			for (i = 0; i < vec1.size(); i++) {
				y = n + vec1[i];
				x = m + vec2[i];

				if (y == 0 && x == 0) {
					MYCP::DebugPrintf("aaa");
				}

				if (0 <= y && y < h) {
					if (0 <= x && x < w) {
						if (record[y][x] == 0) {
							if (field[n][m] == reverse(field[y][x])) {
								stack1.push(y);
								stack2.push(x);
								record[y][x] = 1;


								if (y == 0 && x == 0) {
									MYCP::DebugPrintf("bbb");
								}

								string temp = "";
								temp = temp + to_string(y) + "::" + to_string(x) + " ";
								temp += field[y][x];
								MYCP::DebugPrintf(temp);

								if (field[y][x] == black)c1++;
								else c2++;
							}
						}
					}
				}
			}
		}

		result += (c1 * c2);

		MYCP::DebugPrintf(to_string(c1) + " :: " + to_string(c2));

	}

	cout << result << endl;

	return 0;
}

Measured memory_bytes: 3072