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 C# (Mono 4.6.2.0) (judge verdict: Time Limit Exceeded)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyConsoleAppAlternatingPath1112AC
{
    //
    // AlternatingPath 11.txt  12.txtがACだったプログラムを改良
    // 03,04,10,11,12 AC
    // 01,02,05～09 TLE
    //
    // 構造体
    //
    public struct StartGoal
    {
        public short startpoint_X;
        public short startpoint_Y;
        public short goalpoint_X;
        public short goalpoint_Y;
    }
    class Program
    {
        static char[,] Board;
        static char[,] CheckS;

        static StartGoal sg;

        static List<StartGoal> ls1 = new List<StartGoal>();
        static List<StartGoal> ls2 = new List<StartGoal>();
        static List<StartGoal> ls3 = new List<StartGoal>();

        static void Main(string[] args)
        {
            //
            // 入力
            //
            // スペース区切りの整数の入力
            string[] input = Console.ReadLine().Split(' ');
            short h = short.Parse(input[0]); //高さＨ
            short w = short.Parse(input[1]); //幅Ｗ

            Board = new char[h, w];//２次元配列を準備しておく
            CheckS = new char[h, w];//到達した場所に印を付けておく

            string[] str = new string[h];//マスのデータを受け取る。行数分繰り返す
            // 文字列の入力
            for (int i = 0; i < str.Length; i++)
            {
                str[i] = Console.ReadLine();// 一行分のデータを受け取る
            }
            //
            // ここからプログラム本体
            //
            for (int j = 0; j < h; j++)// 配列の初期化と読み込んだデータのセット
            {
                for (int k = 0; k < w; k++)
                {
                    Board[j, k] = str[j][k];
                    CheckS[j, k] = '?';
                }
            }
            //
            // "#"の位置から"."を探索してみる。見つけた"."の場所には印Gを付ける
            //

            ls1.Clear();// LISTを空にしておいて

            for (short j = 0; j < h; j++)
            {
                for (short k = 0; k < w; k++)
                {
                    if (Board[j, k] == '#')
                    {
                        Surrounding(j, k, h, w);// 周囲を探索するメソッドSurroundingを呼ぶ
                    }
                }
            }
            //
            // 変化がなくなるまでCombineを繰り返す
            //
            int tmp;
            do
            {
                tmp = ls3.Count;
                Combine(h, w);
                ls1.Clear();
                ls1.AddRange(ls3);
            } while (tmp != ls3.Count);
            //
            // 出力
            //
            /*
            foreach (StartGoal element in ls3){
                Console.WriteLine("ls2:{0}, {1}, {2}, {3}",element.startpoint_X,element.startpoint_X,element.goalpoint_X,element.goalpoint_X);
            }
            */
            Console.WriteLine(ls3.Count);
        }
        //
        // Main終わり
        //

        //
        // メソッド　Combine つながるデータはつなげる　#. →　#.#.にできないか調べるメソッド
        //
        public static void Combine(short hh, short ww)
        {
            short temp_X = 0;
            short temp_Y = 0;
            ls2.Clear();
            ls3.Clear();

            for (int p = 0; p < ls1.Count; p++)
            {
                temp_X = ls1[p].goalpoint_X;
                temp_Y = ls1[p].goalpoint_Y;

                if ((ls1[p].goalpoint_X > 0) && (ls1[p].goalpoint_X - 1 != ls1[p].startpoint_X))
                {
                    SetNodeLs2(p, temp_X-1, temp_Y);
                }
                if ((ls1[p].goalpoint_Y > 0) && (ls1[p].goalpoint_Y - 1 != ls1[p].startpoint_Y))
                {
                    SetNodeLs2(p, temp_X, temp_Y-1);
                }
                if ((ls1[p].goalpoint_X < (hh - 1)) && (ls1[p].goalpoint_X + 1 != ls1[p].startpoint_X))
                {
                    SetNodeLs2(p, temp_X+1, temp_Y);
                }
                if ((ls1[p].goalpoint_Y < (ww - 1)) && (ls1[p].goalpoint_Y + 1 != ls1[p].startpoint_Y))
                {
                    SetNodeLs2(p, temp_X,temp_Y+1);
                }
            }
            //
            // 確認
            //
            //Console.WriteLine("-----lsとls2を連結した後-----");
            ls1.AddRange(ls2);
            ls3 = ls1.Distinct().ToList();// 同じものを削除する
        }
        // メソッド　新たなノードをリストls2に書き込む
        public static void SetNodeLs2(int startNode, int temp_X, int temp_Y)
        {
            for (int j = 0; j < ls1.Count; j++)
            {
                if ((ls1[j].startpoint_X == temp_X) && (ls1[j].startpoint_Y == temp_Y))
                {
                    if ((ls1[j].goalpoint_X != ls1[startNode].goalpoint_X) || (ls1[j].goalpoint_Y != ls1[startNode].goalpoint_Y))
                    {
                        //Console.WriteLine(p.ToString()+"と"+q.ToString()+"を連結しましょう１");
                        //StartGoal sg = new StartGoal();
                        sg.startpoint_X = ls1[startNode].startpoint_X;
                        sg.startpoint_Y = ls1[startNode].startpoint_Y;
                        sg.goalpoint_X = ls1[j].goalpoint_X;
                        sg.goalpoint_Y = ls1[j].goalpoint_Y;
                        ls2.Add(sg);
                    }
                }
            }
        }
        //
        // メソッド　座標の前後左右を調べる
        // スタート地点　Ｓ
        // ゴール地点　Ｇ
        // Ｓ→Ｇのペアを調べてリストに登録する
        //
        public static void Surrounding(short j_pos, short k_pos, short hh, short ww)
        {
            char str1 = '.';
            char str2 = 'G';//たどりついた場所に印をつけるスタート地点Ｓ、ゴール地点Ｇ
            //
            CheckS[j_pos, k_pos] = 'S';// スタート地点にＳをつける
                                       //
            if ((j_pos > 0) && (Board[j_pos - 1, k_pos] == str1))// Boardの中身が'.'だったら
            {
                CheckS[j_pos - 1, k_pos] = str2;// Gをセットする
                SetNodeLs1(j_pos, k_pos, (short)(j_pos - 1), k_pos);// リストlsに追加する
            }
            if ((j_pos < (hh - 1)) && (Board[j_pos + 1, k_pos] == str1))
            {
                CheckS[j_pos + 1, k_pos] = str2;// Gをセットする
                SetNodeLs1(j_pos, k_pos, (short)(j_pos +1), k_pos);
            }
            if ((k_pos > 0) && (Board[j_pos, k_pos - 1] == str1))
            {
                CheckS[j_pos, k_pos - 1] = str2;// Gをセットする
                SetNodeLs1(j_pos, k_pos, j_pos, (short)(k_pos-1));
            }
            if ((k_pos < (ww - 1)) && (Board[j_pos, k_pos + 1] == str1))
            {
                CheckS[j_pos, k_pos + 1] = str2;// Gをセットする
                SetNodeLs1(j_pos, k_pos, j_pos, (short)(k_pos+1));
            }
        }
        // メソッド SetNodeLs1
        public static void SetNodeLs1(short start_X, short start_Y, short goal_X, short goal_Y)
        {
            sg.startpoint_X = start_X;
            sg.startpoint_Y = start_Y;
            sg.goalpoint_X = goal_X;
            sg.goalpoint_Y = goal_Y;
            ls1.Add(sg); //リストに追加する
        }
    }
}