#include <bits/stdc++.h>
#include <cmath>
#include <cstddef>
#include <functional>
#include <iostream>
#include <string>
#include <type_traits>

// lib/vector2.hpp
#ifndef VECTOR2_HPP
#define VECTOR2_HPP


/*
usage:

tools::vector2<int>(6, 5) // => (6, 5)
*/

namespace tools {
  template <typename T>
  class vector2 {
  public:
    T x;
    T y;

    vector2() :
      vector2(T(), T()) {
    }

    vector2(const T& x, const T& y) :
      x(x),
      y(y) {
    }

    double norm() const {
      return std::sqrt(this->squaredNorm());
    }

    T squaredNorm() const {
      return this->inner_product(*this);
    }

    template <typename CHECK = T, typename std::enable_if<std::is_same<CHECK, double>::value, std::nullptr_t>::type = nullptr>
    double normalize() const {
      return *this / this->norm();
    }

    vector2<T> operator+() const {
      return *this;
    }

    vector2<T> operator-() const {
      return vector2<T>(-this->x, -this->y);
    }

    friend vector2<T> operator+(const vector2<T>& lhs, const vector2<T>& rhs) {
      return vector2<T>(lhs.x + rhs.x, lhs.y + rhs.y);
    }

    friend vector2<T> operator-(const vector2<T>& lhs, const vector2<T>& rhs) {
      return vector2<T>(lhs.x - rhs.x, lhs.y - rhs.y);
    }

    template <typename T2, typename std::enable_if<!std::is_same<T2, vector2<T>>::value, std::nullptr_t>::type = nullptr>
    friend vector2<T> operator*(const vector2<T>& lhs, const T2& rhs) {
      return vector2<T>(lhs.x * rhs, lhs.y * rhs);
    }
    template <typename T2, typename std::enable_if<!std::is_same<T2, vector2<T>>::value, std::nullptr_t>::type = nullptr>
    friend vector2<T> operator*(const T2& lhs, const vector2<T>& rhs) {
      return vector2<T>(lhs * rhs.x, lhs * rhs.y);
    }

    template <typename T2, typename std::enable_if<!std::is_same<T2, vector2<T>>::value, std::nullptr_t>::type = nullptr>
    friend vector2<T> operator/(const vector2<T>& lhs, const T2& rhs) {
      return vector2<T>(lhs.x / rhs, lhs.y / rhs);
    }

    T inner_product(const vector2<T>& other) const {
      return this->x * other.x + this->y * other.y;
    }

    vector2<T>& operator+=(const vector2<T>& other) {
      return *this = *this + other;
    }

    vector2<T>& operator-=(const vector2<T>& other) {
      return *this = *this - other;
    }

    friend bool operator==(const vector2<T>& lhs, const vector2<T>& rhs) {
      return lhs.x == rhs.x && lhs.y == rhs.y;
    }

    friend bool operator!=(const vector2<T>& lhs, const vector2<T>& rhs) {
      return lhs.x != rhs.x || lhs.y != rhs.y;
    }
  };
}

namespace std {
  template <typename T>
  string to_string(const tools::vector2<T>& f) {
    return '(' + to_string(f.x) + ", " + to_string(f.y) + ')';
  }

  template <typename T>
  struct hash<tools::vector2<T>> {
    typedef std::size_t result_type;
    typedef tools::vector2<T> argument_type;
    std::size_t operator()(const tools::vector2<T>& key) const {
      static const std::hash<T> hash = std::hash<T>();
      std::size_t result = 17;
      result = 31 * result + hash(key.x);
      result = 31 * result + hash(key.y);
      return result;
    }
  };
}

template <typename T>
std::ostream& operator<<(std::ostream& os, const tools::vector2<T>& self) {
  return os << std::to_string(self);
}

#endif

// main.cpp

class bfs_queue {
private:
  std::unordered_set<tools::vector2<std::int_fast64_t>> set;
  std::vector<tools::vector2<std::int_fast64_t>> vector;

public:
  tools::vector2<std::int_fast64_t> operator[](const std::int_fast64_t i) const {
    return this->vector[i];
  }

  std::int_fast64_t size() const {
    return this->vector.size();
  }

  void insert(const tools::vector2<std::int_fast64_t>& x) {
    if (this->set.insert(x).second) {
      this->vector.push_back(x);
    }
  }

  bool contains(const tools::vector2<std::int_fast64_t>& x) const {
    return this->set.find(x) != this->set.end();
  }
};

int main() {
  std::int_fast64_t H, W;
  std::cin >> H >> W;
  std::vector<std::string> S;
  S.reserve(H);
  std::copy_n(std::istream_iterator<std::string>(std::cin), H, std::back_inserter(S));

  std::unordered_set<tools::vector2<std::int_fast64_t>> unvisited_nodes;
  for (std::int_fast64_t i = 0; i < H; ++i) {
    for (std::int_fast64_t j = 0; j < W; ++j) {
      unvisited_nodes.insert(tools::vector2<std::int_fast64_t>(j, i));
    }
  }

  std::int_fast64_t answer = 0;
  while (!unvisited_nodes.empty()) {
    std::int_fast64_t white = 0;
    std::int_fast64_t black = 0;
    bfs_queue bfs;
    bfs.insert(*unvisited_nodes.begin());
    for (std::int_fast64_t i = 0; i < bfs.size(); ++i) {
      const tools::vector2<std::int_fast64_t>& vec = bfs[i];
      switch (S[vec.y][vec.x]) {
      case '.':
        if (0 < vec.y && S[vec.y - 1][vec.x] == '#') bfs.insert(tools::vector2<std::int_fast64_t>(vec.x, vec.y - 1));
        if (vec.y < H - 1 && S[vec.y + 1][vec.x] == '#') bfs.insert(tools::vector2<std::int_fast64_t>(vec.x, vec.y + 1));
        if (0 < vec.x && S[vec.y][vec.x - 1] == '#') bfs.insert(tools::vector2<std::int_fast64_t>(vec.x - 1, vec.y));
        if (vec.x < W - 1 && S[vec.y][vec.x + 1] == '#') bfs.insert(tools::vector2<std::int_fast64_t>(vec.x + 1, vec.y));
        ++white;
        break;
      case '#':
        if (0 < vec.y && S[vec.y - 1][vec.x] == '.') bfs.insert(tools::vector2<std::int_fast64_t>(vec.x, vec.y - 1));
        if (vec.y < H - 1 && S[vec.y + 1][vec.x] == '.') bfs.insert(tools::vector2<std::int_fast64_t>(vec.x, vec.y + 1));
        if (0 < vec.x && S[vec.y][vec.x - 1] == '.') bfs.insert(tools::vector2<std::int_fast64_t>(vec.x - 1, vec.y));
        if (vec.x < W - 1 && S[vec.y][vec.x + 1] == '.') bfs.insert(tools::vector2<std::int_fast64_t>(vec.x + 1, vec.y));
        ++black;
        break;
      }
      unvisited_nodes.erase(vec);
    }
    answer += white * black;
  }

  std::cout << answer << std::endl;
  return 0;
}

Measured memory_bytes: 19524