Buggy python submission (verdict: Runtime Error):
import sys
fin = sys.stdin.readline
sys.setrecursionlimit(100000)
from collections import defaultdict


H, W = [int(elem) for elem in fin().split()]
S = [tuple(elem for elem in fin()[:-1]) for _ in range(H)]
# print(S)

visited = [[False] * W for _ in range(H)]
directions = ((-1, 0), (1, 0), (0, -1), (0, 1))
group_numblack = defaultdict(int)
group_numwhite = defaultdict(int)

def dfs_visit(i, j, visited, group):
    if visited[i][j]:
        return
    visited[i][j] = True
    if S[i][j] == "#":
        group_numblack[group] += 1
    else:
        group_numwhite[group] += 1
    for di, dj in directions:
        next_i, next_j = i + di, j + dj
        if next_i < 0 or next_i >= H or next_j < 0 or next_j >= W:
            continue
        elif visited[next_i][next_j]:
            continue
        elif S[i][j] == S[next_i][next_j]:
            continue
        else:
            dfs_visit(next_i, next_j, visited, group)

# DFS
group = 0
for i in range(H):
    for j in range(W):
        dfs_visit(i, j, visited, group)
        # stack = [(i, j)]
        # while stack:
        #     i, j = stack.pop()
        #     if visited[i][j]:
        #         continue
        #     visited[i][j] = True
        #     if S[i][j] == "#":
        #         group_numblack[group] += 1
        #     else:
        #         group_numwhite[group] += 1
        #     for di, dj in directions:
        #         next_i, next_j = i + di, j + dj
        #         if next_i < 0 or next_i >= H or next_j < 0 or next_j >= W:
        #             continue
        #         elif visited[next_i][next_j]:
        #             continue
        #         elif S[i][j] == S[next_i][next_j]:
        #             continue
        #         else:
        #             stack.append((next_i, next_j))
        group += 1
# print(visited)
# print(group_numblack)
# print(group_numwhite)

num_pairs = 0
for group in group_numblack.keys():
    num_pairs += group_numblack[group] * group_numwhite[group]
print(num_pairs)


Fixed submission:
import sys
fin = sys.stdin.readline
sys.setrecursionlimit(300000)
from collections import defaultdict


H, W = [int(elem) for elem in fin().split()]
S = [tuple(elem for elem in fin()[:-1]) for _ in range(H)]
# print(S)

visited = [[False] * W for _ in range(H)]
directions = ((-1, 0), (1, 0), (0, -1), (0, 1))
group_numblack = defaultdict(int)
group_numwhite = defaultdict(int)

def dfs_visit(i, j, visited, group):
    if visited[i][j]:
        return
    visited[i][j] = True
    if S[i][j] == "#":
        group_numblack[group] += 1
    else:
        group_numwhite[group] += 1
    for di, dj in directions:
        next_i, next_j = i + di, j + dj
        if next_i < 0 or next_i >= H or next_j < 0 or next_j >= W:
            continue
        elif visited[next_i][next_j]:
            continue
        elif S[i][j] == S[next_i][next_j]:
            continue
        else:
            dfs_visit(next_i, next_j, visited, group)

# DFS
group = 0
for i in range(H):
    for j in range(W):
        dfs_visit(i, j, visited, group)
        # stack = [(i, j)]
        # while stack:
        #     i, j = stack.pop()
        #     if visited[i][j]:
        #         continue
        #     visited[i][j] = True
        #     if S[i][j] == "#":
        #         group_numblack[group] += 1
        #     else:
        #         group_numwhite[group] += 1
        #     for di, dj in directions:
        #         next_i, next_j = i + di, j + dj
        #         if next_i < 0 or next_i >= H or next_j < 0 or next_j >= W:
        #             continue
        #         elif visited[next_i][next_j]:
        #             continue
        #         elif S[i][j] == S[next_i][next_j]:
        #             continue
        #         else:
        #             stack.append((next_i, next_j))
        group += 1
# print(visited)
# print(group_numblack)
# print(group_numwhite)

num_pairs = 0
for group in group_numblack.keys():
    num_pairs += group_numblack[group] * group_numwhite[group]
print(num_pairs)

Buggy python submission (verdict: Runtime Error):
import sys
fin = sys.stdin.readline
from collections import defaultdict


H, W = [int(elem) for elem in fin().split()]
S = [tuple(elem for elem in fin()[:-1]) for _ in range(H)]
# print(S)

visited = [[False] * W for _ in range(H)]
directions = ((-1, 0), (1, 0), (0, -1), (0, 1))
group_numblack = defaultdict(int)
group_numwhite = defaultdict(int)

def dfs_visit(i, j, visited, group):
    if visited[i][j]:
        return
    visited[i][j] = True
    if S[i][j] == "#":
        group_numblack[group] += 1
    else:
        group_numwhite[group] += 1
    for di, dj in directions:
        next_i, next_j = i + di, j + dj
        if next_i < 0 or next_i >= H or next_j < 0 or next_j >= W:
            continue
        elif visited[next_i][next_j]:
            continue
        elif S[i][j] == S[next_i][next_j]:
            continue
        else:
            dfs_visit(next_i, next_j, visited, group)

# DFS
group = 0
for i in range(H):
    for j in range(W):
        dfs_visit(i, j, visited, group)
        # stack = [(i, j)]
        # while stack:
        #     i, j = stack.pop()
        #     if visited[i][j]:
        #         continue
        #     visited[i][j] = True
        #     if S[i][j] == "#":
        #         group_numblack[group] += 1
        #     else:
        #         group_numwhite[group] += 1
        #     for di, dj in directions:
        #         next_i, next_j = i + di, j + dj
        #         if next_i < 0 or next_i >= H or next_j < 0 or next_j >= W:
        #             continue
        #         elif visited[next_i][next_j]:
        #             continue
        #         elif S[i][j] == S[next_i][next_j]:
        #             continue
        #         else:
        #             stack.append((next_i, next_j))
        group += 1
# print(visited)
# print(group_numblack)
# print(group_numwhite)

num_pairs = 0
for group in group_numblack.keys():
    num_pairs += group_numblack[group] * group_numwhite[group]
print(num_pairs)


Fixed submission:
import sys
fin = sys.stdin.readline
sys.setrecursionlimit(300000)
from collections import defaultdict


H, W = [int(elem) for elem in fin().split()]
S = [tuple(elem for elem in fin()[:-1]) for _ in range(H)]
# print(S)

visited = [[False] * W for _ in range(H)]
directions = ((-1, 0), (1, 0), (0, -1), (0, 1))
group_numblack = defaultdict(int)
group_numwhite = defaultdict(int)

def dfs_visit(i, j, visited, group):
    if visited[i][j]:
        return
    visited[i][j] = True
    if S[i][j] == "#":
        group_numblack[group] += 1
    else:
        group_numwhite[group] += 1
    for di, dj in directions:
        next_i, next_j = i + di, j + dj
        if next_i < 0 or next_i >= H or next_j < 0 or next_j >= W:
            continue
        elif visited[next_i][next_j]:
            continue
        elif S[i][j] == S[next_i][next_j]:
            continue
        else:
            dfs_visit(next_i, next_j, visited, group)

# DFS
group = 0
for i in range(H):
    for j in range(W):
        dfs_visit(i, j, visited, group)
        # stack = [(i, j)]
        # while stack:
        #     i, j = stack.pop()
        #     if visited[i][j]:
        #         continue
        #     visited[i][j] = True
        #     if S[i][j] == "#":
        #         group_numblack[group] += 1
        #     else:
        #         group_numwhite[group] += 1
        #     for di, dj in directions:
        #         next_i, next_j = i + di, j + dj
        #         if next_i < 0 or next_i >= H or next_j < 0 or next_j >= W:
        #             continue
        #         elif visited[next_i][next_j]:
        #             continue
        #         elif S[i][j] == S[next_i][next_j]:
        #             continue
        #         else:
        #             stack.append((next_i, next_j))
        group += 1
# print(visited)
# print(group_numblack)
# print(group_numwhite)

num_pairs = 0
for group in group_numblack.keys():
    num_pairs += group_numblack[group] * group_numwhite[group]
print(num_pairs)