use std::io::{stdin, Read, StdinLock};
use std::str::FromStr;
use std::collections::{HashSet, HashMap};

fn main() {
    let cin = stdin();
    let mut scan = Scanner::new(cin.lock());

    let h = scan.read::<usize>();
    let w = scan.read::<usize>();
    let mut s = vec![];
    for _ in 0..h {s.push(scan.chars())};

    let mut utree = Utree::new(h*w);
    for y in 0..h { for x in 0..w {
        let i = y * w + x;
        for (ny, nx) in adjacent4(y, x, h, w) {
            let j = ny * w + nx;
            if s[y][x] != s[ny][nx] {utree.unite(i, j)}
        }
    }}
    let mut cnt = HashMap::new();
    for y in 0..h { for x in 0..w {
        let i = y * w + x;
        let j = if s[y][x] == '.' {0} else {1};
        (*cnt.entry(utree.find(i)).or_insert(vec![0;2]))[j] += 1;
    }}
    let mut ret = 0i64;
    for (_, v) in &cnt {
        ret += v[0] * v[1];
    }
    println!("{}", ret);
}

/// https://github.com/hatoo/competitive-rust-snippets
#[allow(dead_code)]
fn adjacent4(y: usize, x: usize, h: usize, w: usize) -> Box<Iterator<Item = (usize, usize)>> {
    // This code looks verbose but is needed to Rust 1.15.
    static DYDX: [(isize, isize); 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];

    Box::new(DYDX.iter().filter_map(move |&(dy, dx)| {
        let ny = y as isize + dy;
        let nx = x as isize + dx;
        if nx >= 0 && nx < w as isize && ny >= 0 && ny < h as isize {
            Some((ny as usize, nx as usize))
        } else {
            None
        }
    }))
}

#[allow(dead_code)]
pub struct Utree {
    n: usize,
    par: Vec<usize>,
    rank: Vec<usize>,
    size: Vec<usize>,
}


#[allow(dead_code)]
impl Utree {
    pub fn new(n: usize) -> Utree {
        Utree {
            n: n,
            par: (0..n).collect(),
            rank: vec![0; n],
            size: vec![1; n],
        }
    }

    pub fn find(&mut self, x: usize) -> usize {
        if self.par[x] == x {
            x
        } else {
            let parent = { self.par[x] };
            let ret = self.find(parent);
            self.par[x] = ret;
            ret
        }
    }

    pub fn unite(&mut self, mut x: usize, mut y: usize) {
        x = self.find(x);
        y = self.find(y);
        if x == y {
            return;
        };

        if self.rank[x] < self.rank[y] {
            self.par[x] = y;
            self.size[y] += self.size[x];
        } else {
            self.par[y] = x;
            self.size[x] += self.size[y];
            if self.rank[x] == self.rank[y] {
                self.rank[x] += 1;
            }
        }
    }

    pub fn is_same(&mut self, mut x: usize, mut y: usize) -> bool {
        x = self.find(x);
        y = self.find(y);
        x == y
    }

    pub fn get_size(&mut self, mut x: usize) -> usize {
        x = self.find(x);
        self.size[x]
    }

    fn size(&mut self) -> usize {
        let mut hset = HashSet::new();
        for i in 0..self.n {hset.insert(self.find(i));}
        hset.len()
    }
}

// region template
#[allow(dead_code)]
fn iu(i: isize) -> usize { i as usize }

#[allow(dead_code)]
fn ui(i: usize) -> isize { i as isize }

#[allow(dead_code, deprecated)]
fn join<T: std::fmt::Display>(slice: &[T], sep: &str) -> String {
    let strings = slice.iter().map(|t| format!("{}", t)).collect::<Vec<_>>();
    strings.connect(sep)
}

struct Scanner<'a> {
    cin: StdinLock<'a>,
}

#[allow(dead_code)]
impl<'a> Scanner<'a> {
    fn new(cin: StdinLock<'a>) -> Scanner<'a> {
        Scanner { cin: cin }
    }

    fn read1<T: FromStr>(&mut self) -> Option<T> {
        let token = self.cin.by_ref().bytes().map(|c| c.unwrap() as char)
            .skip_while(|c| c.is_whitespace())
            .take_while(|c| !c.is_whitespace())
            .collect::<String>();
        token.parse::<T>().ok()
    }

    fn read<T: FromStr>(&mut self) -> T {
        self.read1().unwrap()
    }

    fn readn<T: FromStr>(&mut self, n: usize) -> Vec<T> {
        let mut ret = vec![];
        for _ in 0..n { ret.push(self.read::<T>()) }
        ret
    }

    fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}
// endregion

Measured memory_bytes: 26956