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;// Listを使う
using System.Linq;
namespace MyConsoleAppAlternatimgPath3
{
    //
    // AlternatingPath3
    //
    class Program
    {
        //
        // 構造体
        //
        public struct StartGoal
        {
            public int startpoint_X;
            public int startpoint_Y;
            public int goalpoint_X;
            public int goalpoint_Y;
        }
        //
        static string[,] Board;
        static string[,] CheckS;
        static List<StartGoal> ls = 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(' ');
            int h = int.Parse(input[0]); //高さＨ
            int w = int.Parse(input[1]); //幅Ｗ

            Board = new string[h, w];//２次元配列を準備しておく
            CheckS = new string[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].Substring(k, 1);
                    CheckS[j, k] = "?";
                }
            }
            //
            // "#"の位置から"."を探索してみる。見つけた"."の場所には印Gを付ける
            //

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

            for (int j = 0; j < h; j++)
            {
                for (int k = 0; k < w; k++)
                {
                    if (Board[j, k] == "#")
                    {
                        Surrounding(j, k,h,w);// 周囲を探索するメソッドSurroundingを呼ぶ
                    }
                }
            }
            //
            // 変化がなくなるまでConbineを繰り返す
            //
            int tmp;
            do
            {
                tmp = ls3.Count;
                Conbine(h, w);
                ls.Clear();
                ls.AddRange(ls3);
            } while (tmp != ls3.Count);
            //
            // 出力
            //
            Console.WriteLine(ls3.Count);
        }
        //
        // Main終わり
        //



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

            for (int p = 0; p < ls.Count; p++)
            {
                if ((ls[p].goalpoint_X > 0) && (ls[p].goalpoint_X - 1 != ls[p].startpoint_X))
                {
                    temp_X = ls[p].goalpoint_X - 1;
                    temp_Y = ls[p].goalpoint_Y;
                    for (int q = 0; q < ls.Count; q++)
                    {
                        if ((ls[q].startpoint_X == temp_X) && (ls[q].startpoint_Y == temp_Y))
                        {
                            if ((ls[q].goalpoint_X != ls[p].goalpoint_X) || (ls[q].goalpoint_Y != ls[p].goalpoint_Y))
                            {
                                //Console.WriteLine(p.ToString()+"と"+q.ToString()+"を連結しましょう１");
                                StartGoal sg = new StartGoal();
                                sg.startpoint_X = ls[p].startpoint_X;
                                sg.startpoint_Y = ls[p].startpoint_Y;
                                sg.goalpoint_X = ls[q].goalpoint_X;
                                sg.goalpoint_Y = ls[q].goalpoint_Y;
                                ls2.Add(sg);
                            }
                        }
                    }
                }
                if ((ls[p].goalpoint_Y > 0) && (ls[p].goalpoint_Y - 1 != ls[p].startpoint_Y))
                {
                    temp_X = ls[p].goalpoint_X;
                    temp_Y = ls[p].goalpoint_Y - 1;
                    for (int q = 0; q < ls.Count; q++)
                    {
                        if ((ls[q].startpoint_X == temp_X) && (ls[q].startpoint_Y == temp_Y))
                        {
                            if ((ls[q].goalpoint_X != ls[p].goalpoint_X) || (ls[q].goalpoint_Y != ls[p].goalpoint_Y))
                            {
                                //Console.WriteLine(p.ToString() + "と" + q.ToString() + "を連結しましょう２");
                                StartGoal sg = new StartGoal();
                                sg.startpoint_X = ls[p].startpoint_X;
                                sg.startpoint_Y = ls[p].startpoint_Y;
                                sg.goalpoint_X = ls[q].goalpoint_X;
                                sg.goalpoint_Y = ls[q].goalpoint_Y;
                                ls2.Add(sg);
                            }
                        }
                    }
                }
                if ((ls[p].goalpoint_X < (hh - 1)) && (ls[p].goalpoint_X + 1 != ls[p].startpoint_X))
                {
                    temp_X = ls[p].goalpoint_X + 1;
                    temp_Y = ls[p].goalpoint_Y;
                    for (int q = 0; q < ls.Count; q++)
                    {
                        if ((ls[q].startpoint_X == temp_X) && (ls[q].startpoint_Y == temp_Y))
                        {
                            if ((ls[q].goalpoint_X != ls[p].goalpoint_X) || (ls[q].goalpoint_Y != ls[p].goalpoint_Y))
                            {
                                //Console.WriteLine(p.ToString() + "と" + q.ToString() + "を連結しましょう３");
                                StartGoal sg = new StartGoal();
                                sg.startpoint_X = ls[p].startpoint_X;
                                sg.startpoint_Y = ls[p].startpoint_Y;
                                sg.goalpoint_X = ls[q].goalpoint_X;
                                sg.goalpoint_Y = ls[q].goalpoint_Y;
                                ls2.Add(sg);
                            }
                        }
                    }
                }
                if ((ls[p].goalpoint_Y < (ww - 1)) && (ls[p].goalpoint_Y + 1 != ls[p].startpoint_Y))
                {
                    temp_X = ls[p].goalpoint_X;
                    temp_Y = ls[p].goalpoint_Y + 1;
                    for (int q = 0; q < ls.Count; q++)
                    {
                        if ((ls[q].startpoint_X == temp_X) && (ls[q].startpoint_Y == temp_Y))
                        {
                            if ((ls[q].goalpoint_X != ls[p].goalpoint_X) || (ls[q].goalpoint_Y != ls[p].goalpoint_Y))
                            {
                                //Console.WriteLine(p.ToString() + "と" + q.ToString() + "を連結しましょう４");
                                StartGoal sg = new StartGoal();
                                sg.startpoint_X = ls[p].startpoint_X;
                                sg.startpoint_Y = ls[p].startpoint_Y;
                                sg.goalpoint_X = ls[q].goalpoint_X;
                                sg.goalpoint_Y = ls[q].goalpoint_Y;
                                ls2.Add(sg);
                            }
                        }
                    }
                }
            }
            //
            // 確認
            //
            //Console.WriteLine("-----lsとls2を連結した後-----");
            ls.AddRange(ls2);
            ls3 = ls.Distinct().ToList();// 同じものを削除する
        }

//
// メソッド　座標の前後左右を調べる
// スタート地点　Ｓ
// ゴール地点　Ｇ
 // Ｓ→Ｇのペアを調べてリストに登録する
 //
        public static void Surrounding(int j_pos, int k_pos,int hh,int ww)
        {
            string str1 = ".";
            string str2 = "G";//たどりついた場所に印をつけるスタート地点Ｓ、ゴール地点Ｇ
                              //
            CheckS[j_pos, k_pos] = "S";// スタート地点にＳをつける
                                       //
            if ((j_pos > 0) && (Board[j_pos - 1, k_pos] == str1))
            {
                CheckS[j_pos - 1, k_pos] = str2;// ゴール地点にＧをつける
                StartGoal sg = new StartGoal();
                sg.startpoint_X = j_pos;
                sg.startpoint_Y = k_pos;
                sg.goalpoint_X = j_pos - 1;
                sg.goalpoint_Y = k_pos;
                ls.Add(sg);
            }
            if ((j_pos < (hh - 1)) && (Board[j_pos + 1, k_pos] == str1))
            {
                CheckS[j_pos + 1, k_pos] = str2;
                StartGoal sg = new StartGoal();
                sg.startpoint_X = j_pos;
                sg.startpoint_Y = k_pos;
                sg.goalpoint_X = j_pos + 1;
                sg.goalpoint_Y = k_pos;
                ls.Add(sg);
            }
            if ((k_pos > 0) && (Board[j_pos, k_pos - 1] == str1))
            {
                CheckS[j_pos, k_pos - 1] = str2;
                StartGoal sg = new StartGoal();
                sg.startpoint_X = j_pos;
                sg.startpoint_Y = k_pos;
                sg.goalpoint_X = j_pos;
                sg.goalpoint_Y = k_pos - 1;
                ls.Add(sg);
            }
            if ((k_pos < (ww - 1)) && (Board[j_pos, k_pos + 1] == str1))
            {
                CheckS[j_pos, k_pos + 1] = str2;
                StartGoal sg = new StartGoal();
                sg.startpoint_X = j_pos;
                sg.startpoint_Y = k_pos;
                sg.goalpoint_X = j_pos;
                sg.goalpoint_Y = k_pos + 1;
                ls.Add(sg);
            }
        }
    }
}