import std.algorithm;
import std.array;
import std.conv;
import std.math;
import std.range;
import std.stdio;
import std.string;
import std.typecons;

T read(T)() { return readln.chomp.to!T; }
T[] reads(T)() { return readln.split.to!(T[]); }
alias readint = read!int;
alias readints = reads!int;

long calc(string[] g) {
    int rows = cast(int) g.length;
    int cols = cast(int) g[0].length;

    auto hash = (int row, int col) => row * cols + col;

    auto uf = new UnionFind(rows * cols);
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (i + 1 < rows && g[i][j] != g[i + 1][j])
                uf.unite(hash(i, j), hash(i+1, j));

            if (j + 1 < cols && g[i][j] != g[i][j + 1])
                uf.unite(hash(i, j), hash(i, j+1));
        }
    }

    long ans = 0;
    bool[int] used;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            int root = uf.root(hash(i, j));
            if (root in used) continue;
            used[root] = true;

            long a = 0; // 黒の個数
            long b = 0; // 白の個数
            // root に属する全てのノード
            uf.doEach(root, (node) {
                int row = node / cols;
                int col = node % cols;
                if (g[row][col] == '#') a++; else b++;
            });
            ans += a * b;
        }
    }
    return ans;
}

void main() {
    auto hw = readints;
    int h = hw[0], w = hw[1];
    string[] g;
    for (int i = 0; i < h; i++) {
        g ~= read!string;
    }
    writeln(calc(g));
}

class UnionFind {
    private int[] _data;
    private int[] _nexts; // 次の要素。なければ -1
    private int[] _tails; // 末尾の要素

    this(int n) {
        _data  = new int[](n + 1);
        _nexts = new int[](n + 1);
        _tails = new int[](n + 1);

        _data[] = -1;
        _nexts[] = -1;
        for (int i = 0; i < _tails.length; i++)
            _tails[i] = i;
    }

    int root(int a) {
        if (_data[a] < 0) return a;
        return _data[a] = root(_data[a]);
    }

    bool unite(int a, int b) {
        int ra = root(a);
        int rb = root(b);
        if (ra != rb) {
            if (_data[rb] < _data[ra]) swap(ra, rb);
            _data[ra] += _data[rb]; // ra に rb を繋げる
            _data[rb] = ra;
            // ra の末尾の後ろに rb を繋げる
            _nexts[_tails[ra]] = rb;
            // ra の末尾が rb の末尾になる
            _tails[ra] = _tails[rb];
        }
        return ra != rb;
    }

    bool isSame(int a, int b) {
        return root(a) == root(b);
    }

    /// a が属する集合のサイズ
    int groupSize(int a) {
        return -_data[root(a)];
    }

    /// a が属する集合の要素全て
    void doEach(int a, void delegate(int) fn) {
        auto node = root(a);
        while (node != -1) {
            fn(node);
            node = _nexts[node];
        }
    }
}
