Problem:
Surrounding Area

土地囲い

English text is not available in this practice contest.

ある 2 人の不動産屋が，客船に乗って南の島に向かっていた．青い空，さわやかな風…．2 人は他の乗客とともに船旅を楽しんでいた．ところが，ある日突然発生した竜巻によって客船が沈んでしまった．他の乗客は救助隊によって救助されたが，どういうわけかこの 2 人だけ見落とされてしまった．数日の漂流の後，彼らはある無人島に流れ着いた．この無人島は長方形であり，以下の図のように格子に区切られていた．

図 C-1: 無人島の形状

彼らはとても欲深かったので，助けを呼ぶことではなく，この無人島の土地を売ることを考えていた．そして，彼らは土地を折半するということはせず，土地の奪い合いを始めた．彼らはそれぞれ自分の土地にしたいところを，一方は黒い杭で，もう一方は白い杭で囲み始めた．全ての杭は格子の中央に打たれ，また 1 つの格子に複数の杭が打たれることはなかった．しばらくすると双方とも疲れ果てて，杭を打つことを止めた．

あなたの仕事は，黒い杭および白い杭で囲まれた土地の面積を求めるプログラムを書くことである．ただし，格子 (i, j) が黒い杭に囲まれているとは，直感的には「格子 (i, j) から上下左右に任意に移動したとき，最初に当たる杭は常に黒い杭である」ことを意味する．正確には以下のように定義される．

黒い杭に「拡大隣接した」格子を次のとおりに定める．白い杭についても同様とする．

格子 (i, j) に杭が存在せず，さらに格子 (i, j) に隣接する格子のいずれかに黒い杭が存在するならば，格子 (i, j) は黒い杭に拡大隣接している．

格子 (i, j) に杭が存在せず，さらに格子 (i, j) に隣接する格子のいずれかが黒い杭に拡大隣接しているならば，格子 (i, j) は黒い杭に拡大隣接している．このルールは再帰的に適用される．

このとき，格子 (i, j) が黒い杭に拡大隣接していて，さらに白い杭に隣接していないとき，またそのときに限って，格子 (i, j) が黒い杭に囲まれているという．また逆に，格子 (i, j) が白い杭に拡大隣接していて，さらに黒い杭に隣接していないとき，またそのときに限って，格子 (i, j) が白い杭に囲まれているという．

Input

入力は複数のデータセットから構成される．各データセットは以下のような構成になっている．

w h

a1,1 a2,1 a3,1 ... aw,1

a1,2 a2,2 a3,2 ... aw,2

...

a1,h a2,h a3,h ... aw,h

w は土地の横幅，h は土地の縦幅を表す．これらは 1 ≦ w,h ≦ 50 を満たす．各 ai,j は格子 (i, j) の状態を表す 1 つの半角文字であり，「B」は黒い杭が打たれていることを，「W」は白い杭が打たれていることを，「.」（ピリオド）はいずれの杭も打たれていないことをそれぞれ示す．

w = h = 0 は入力の終端を表しており，データセットには含まれない．

Output

各データセットについて，黒い杭が囲んだ土地の大きさ，および白い杭が囲んだ土地の大きさを 1 つの空白で区切って 1 行に出力しなさい．

Sample Input

10 10
.....W....
....W.W...
...W...W..
....W...W.
.....W...W
......W.W.
BBB....W..
..B..BBBBB
..B..B....
..B..B..W.
5 3
...B.
...BB
.....
1 1
.
0 0

Output for the Sample Input

6 21
12 0
0 0

Solution in C# (judge verdict: Accepted)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using static System.Math;
using MethodImplAttribute = System.Runtime.CompilerServices.MethodImplAttribute;
using MethodImplOptions = System.Runtime.CompilerServices.MethodImplOptions;

public static class P
{
    const int PACKET_SIZE = 512;
    public static void Main()
    {
        while (true)
        {
            var wh = Console.ReadLine().Split().Select(int.Parse).ToArray();
            var w = wh[0];
            var h = wh[1];
            if (w == 0) break;

            var map = Enumerable.Repeat(0, h).SelectMany(_ => Console.ReadLine()).ToArray();

            List<int>[] graph = Enumerable.Repeat(0, h * w).Select(_ => new List<int>()).ToArray();
            for (int i = 0; i < h - 1; i++)
                for (int j = 0; j < w; j++)
                { var id = i * w + j; graph[id].Add(id + w); graph[id + w].Add(id); }
            for (int i = 0; i < h; i++)
                for (int j = 0; j < w - 1; j++)
                { var id = i * w + j; graph[id].Add(id + 1); graph[id + 1].Add(id); }

            int[] flag = new int[w * h];
            UnionFind uf = new UnionFind(w * h);
            for (int i = 0; i < graph.Length; i++)
            {
                if (map[i] != '.') continue;
                foreach (var j in graph[i])
                {
                    if (map[j] == '.')
                    {
                        uf.TryUnite(i, j);
                    }
                    if (map[j] == 'B')
                    {
                        flag[i] |= 1;
                    }
                    if (map[j] == 'W')
                    {
                        flag[i] |= 2;
                    }
                }
            }

            int res1 = 0;
            int res2 = 0;
            foreach (var item in Enumerable.Range(0, w * h).GroupBy(x => uf.Find(x)))
            {
                var count = item.Count();
                var a = item.Aggregate(0, (x, y) => x | flag[y]);
                if (a == 1) res1 += count;
                if (a == 2) res2 += count;
            }

            Console.WriteLine($"{res1} {res2}");
        }
    }
}


class UnionFind
{
    public int Size { get; private set; }
    public int GroupCount { get; private set; }
    public IEnumerable<int> AllRepresents => Parent.Where((x, y) => x == y);
    int[] Sizes;
    int[] Parent;
    public UnionFind(int count)
    {
        Size = count;
        GroupCount = count;
        Parent = new int[count];
        Sizes = new int[count];
        for (int i = 0; i < count; i++) Sizes[Parent[i] = i] = 1;
    }
    public bool TryUnite(int x, int y)
    {
        int xp = Find(x);
        int yp = Find(y);
        if (yp == xp) return false;
        if (Sizes[xp] < Sizes[yp]) { var tmp = xp; xp = yp; yp = tmp; }
        GroupCount--;
        Parent[yp] = xp;
        Sizes[xp] += Sizes[yp];
        return true;
    }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int GetSize(int x) => Sizes[Find(x)];
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int Find(int x)
    {
        while (x != Parent[x]) x = (Parent[x] = Parent[Parent[x]]);
        return x;
    }
}