### BUGGY CODE ###

# -*- coding: utf-8 -*-
import sys
import Queue

def io_generator():
	while True:
		yield raw_input()

#++++++

def dataIo(io):
	# スペース区切りの整数の入力
	h,w = map(int, io.next().split())
	d=[[toBin(c) for c in list(io.next())] for _ in range(h)]
	
	return h,w,d

def toBin(a_char):
	return 1 if a_char=='#' else 0

def mkb(n):
	return [0 for _ in range(n)]

def main(io):
	h,w,d=dataIo(io)
	
	arrived = lambda : [mkb(w) for _ in range(h)]
	ret=0
	#print arrived
	#print d
	preComputed=arrived()
	for x in range(h):
		for y in range(w):
			if d[x][y]==1:
				continue
				
			aa= preComputed[x][y]
			if aa>0:
				continue
			
			a=arrived()
			a[x][y]=1
			temp_ret=0
			temp_sharp=1
			
			openlist=Queue.Queue()
			openlist.put((0,x+1,y+0))
			openlist.put((0,x+0,y+1))
			openlist.put((0,x-1,y-0))
			openlist.put((0,x-0,y-1))
			while not openlist.empty():
				pre, xi, yi = openlist.get()
				preState=1 if pre==0 else 0
				#範囲外なら評価しない。
				if xi < 0 or yi < 0 or xi >= h or yi >= w:
					continue
				else:
					vv=d[xi][yi]
					
				#到達済みなら評価しない
				if a[xi][yi]==1:
					continue
				a[xi][yi]=1
				
				#交互でなければ評価しない
				if vv!=preState:
					continue
				
				#0ならretに1追加
				if vv==1:
					temp_ret+=1
				else:
					temp_sharp+=1
					preComputed[xi][yi]=1
				
				#移動可能な場所を追加
				openlist.put((preState,xi+1,yi+0))
				openlist.put((preState,xi+0,yi+1))
				openlist.put((preState,xi-1,yi-0))
				openlist.put((preState,xi-0,yi-1))
				
			#結果を記載
			ret+=temp_ret*temp_sharp
				
	return ret

#+++++
if __name__ == "__main__":
	io=io_generator()
	print main(io)



### FIXED CODE ###

import sys
import collections

input_methods=['clipboard','file','key']
using_method=0
input_method=input_methods[using_method]

tin=lambda : map(int, input().split())
lin=lambda : list(tin())
mod=1000000007

#+++++

def check_path(mp, is_archive, wi, hi):
	w = len(mp[0])
	h = len(mp)
	nb = 0
	nw = 0
	qq=collections.deque()
	st_color = mp[hi][wi]
	nb+=1
	qq.append((wi, hi, st_color))
	is_archive[hi][wi] = 1
	while qq:
		pw, ph, pc = qq.popleft()
		for dw, dh in ((1,0), (-1,0), (0,1),(0,-1)):
			if pw+dw<0 or pw+dw>=w or ph+dh < 0 or ph+dh >= h:
				continue
			if is_archive[ph+dh][pw+dw]>0:
				continue
			if mp[ph+dh][pw+dw]==pc:
				continue
			nc = mp[ph+dh][pw+dw]
			if nc == st_color:
				nb += 1
			else:
				nw += 1
			
			qq.append((pw+dw, ph+dh, nc))
			is_archive[ph+dh][pw+dw]=1
	
	return nb*nw

def main():
	#a = int(input())
	h, w = tin()
	#s = input()
	mp = [input() for _ in range(h)]
	is_archive = [[-1]*w for _ in range(h)]
	ret = 0
	for wi in range(w):
		for hi in range(h):
			if is_archive[hi][wi] != -1:
				continue
			nn = check_path(mp, is_archive, wi, hi)
			ret += nn
	print(ret)
			
	
	
#+++++
isTest=False

def pa(v):
	if isTest:
		print(v)
		
def input_clipboard():
	import clipboard
	input_text=clipboard.get()
	input_l=input_text.splitlines()
	for l in input_l:
		yield l

if __name__ == "__main__":
	if sys.platform =='ios':
		if input_method==input_methods[0]:
			ic=input_clipboard()
			input = lambda : ic.__next__()
		elif input_method==input_methods[1]:
			sys.stdin=open('inputFile.txt')
		else:
			pass
		isTest=True
	else:
		pass
		#input = sys.stdin.readline
			
	ret = main()
	if ret is not None:
		print(ret)