Problem:
Score : 300 points

Problem Statement

There is a grid with H rows and W columns, where each square is painted black or white.

You are given H strings S_1, S_2, ..., S_H, each of length W.
If the square at the i-th row from the top and the j-th column from the left is painted black, the j-th character in the string S_i is #; if that square is painted white, the j-th character in the string S_i is ..

Find the number of pairs of a black square c_1 and a white square c_2 that satisfy the following condition:

There is a path from the square c_1 to the square c_2 where we repeatedly move to a vertically or horizontally adjacent square through an alternating sequence of black and white squares: black, white, black, white...

Constraints

1 \leq H, W \leq 400

|S_i| = W (1 \leq i \leq H)

For each i (1 \leq i \leq H), the string S_i consists of characters # and ..

Input

Input is given from Standard Input in the following format:

H W
S_1
S_2
:
S_H

Output

Print the answer.

Sample Input 1

3 3
.#.
..#
#..

Sample Output 1

10

Some of the pairs satisfying the condition are ((1, 2), (3, 3)) and ((3, 1), (3, 2)), where (i, j) denotes the square at the i-th row from the top and the j-th column from the left.

Sample Input 2

2 4
....
....

Sample Output 2

0

Sample Input 3

4 3
###
###
...
###

Sample Output 3

6

Solution in Kotlin (1.3.71) (judge verdict: Accepted)

import java.io.BufferedReader

fun main() = aising2019c()

@OptIn(ExperimentalStdlibApi::class)
fun aising2019c() = SimpleIO_aising2019c().exec {
    val (h, w) = readLine().split(" ").map { it.toInt() }
    val board = Array(h) { readLine().map { it == '#' }.toBooleanArray() }

    val around = listOf(-1 to 0, 1 to 0, 0 to -1, 0 to 1)
    val visited = Array(h) { BooleanArray(w) }
    var answer = 0L

    for (i in 0 until h) for (j in 0 until w) {
        if (!board[i][j] || visited[i][j]) continue
        visited[i][j] = true

        var blackCount = 0L
        var whiteCount = 0L

        val queue = ArrayDeque(listOf(i to j))

        while (queue.isNotEmpty()) {
            val (y, x) = queue.removeFirst()
            if (board[y][x]) blackCount++ else whiteCount++
            queue += around.map { y - it.first to x - it.second }
                .filter { (ny, nx) ->
                    ny in 0 until h && nx in 0 until w && board[ny][nx] != board[y][x] &&
                            visited.let {
                                if (it[ny][nx]) false else {
                                    it[ny][nx] = true
                                    true
                                }
                            }
                }
        }

        answer += blackCount * whiteCount
    }

    println(answer)
}

// region kokokara template dayo (^o^)
@Suppress("unused", "HasPlatformType", "MemberVisibilityCanBePrivate", "ClassName")
private class SimpleIO_aising2019c(private val separator: String = System.lineSeparator()) {
    private val br: BufferedReader = System.`in`.bufferedReader()
    private val sb: StringBuilder = StringBuilder()
    fun readLine(): String = br.readLine()!!
    fun println(): Unit = run { sb.append(separator) }
    fun print(any: Any): Unit = run { sb.append(any) }
    fun println(any: Any): Unit = print(any.toString() + separator)
    fun exec(action: SimpleIO_aising2019c.() -> Unit) = run {
        Thread(null, { action() }, "(^o^)", 128 * 1024 * 1024).apply { start() }.join()
    }.run { kotlin.io.print(sb) }
}
// endregion kokomade template dayo (^o^)