#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
using namespace std;
struct Edge {
int u, v; // Indeksy wierzchołków (pól)
int weight; // Waga: 0 dla Tui (T), 1 dla Cisu (C)
int r_out, c_out; // Współrzędne w mapie wyjściowej
bool is_cis; // Czy oryginalnie to był Cis
};
struct DSU {
vector<int> parent;
DSU(int n) {
parent.resize(n);
iota(parent.begin(), parent.end(), 0);
}
int find(int i) {
if (parent[i] == i)
return i;
return parent[i] = find(parent[i]);
}
bool unite(int i, int j) {
int root_i = find(i);
int root_j = find(j);
if (root_i != root_j) {
parent[root_i] = root_j;
return true;
}
return false;
}
};
bool compareEdges(const Edge& a, const Edge& b) {
return a.weight < b.weight;
}
int main() {
// Optymalizacja I/O
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int m, n;
if (!(cin >> m >> n)) return 0;
vector<Edge> edges;
int total_cis_available = 0;
vector<string> result_map;
result_map.reserve(2 * m - 1);
for (int i = 0; i < m; ++i) {
string row;
cin >> row;
result_map.push_back(string(n - 1, 'Z')); // Placeholder na mapie
for (int j = 0; j < n - 1; ++j) {
int u = i * n + j;
int v = i * n + (j + 1);
bool is_cis = (row[j] == 'C');
if (is_cis) total_cis_available++;
// Jeśli to Cis, waga 1 (kosztowne w ścieżce), jeśli Tuja waga 0 (tanie w ścieżce)
edges.push_back({u, v, is_cis ? 1 : 0, 2 * i, j, is_cis});
}
}
// 2. Wczytywanie krawędzi POZIOMYCH (między wierszami i i i+1)
// Jest ich m-1 wierszy, po n znaków
for (int i = 0; i < m - 1; ++i) {
string row;
cin >> row;
result_map.insert(result_map.begin() + (2 * i + 1), string(n, 'Z')); // Placeholder
for (int j = 0; j < n; ++j) {
int u = i * n + j;
int v = (i + 1) * n + j;
bool is_cis = (row[j] == 'C');
if (is_cis) total_cis_available++;
edges.push_back({u, v, is_cis ? 1 : 0, 2 * i + 1, j, is_cis});
}
}
// Sortowanie krawędzi: najpierw te o wadze 0 (Tuja), potem 1 (Cis)
sort(edges.begin(), edges.end(), compareEdges);
DSU dsu(m * n);
int edges_in_tree = 0;
int cis_used_in_path = 0;
int path_edges_count = 0;
// Algorytm Kruskala
for (const auto& edge : edges) {
if (dsu.unite(edge.u, edge.v)) {
// Ta krawędź staje się częścią ścieżki (usuwamy żywopłot)
result_map[edge.r_out][edge.c_out] = '.';
edges_in_tree++;
if (edge.is_cis) {
cis_used_in_path++;
}
}
}
// Obliczenia końcowe
// Liczba wszystkich możliwych krawędzi wewnątrz
long long total_edges_count = edges.size();
// Liczba krawędzi, które zostały żywopłotami = Wszystkie - Te w ścieżce
long long final_hedges_count = total_edges_count - edges_in_tree;
// Liczba cisu w żywopłocie = Cały dostępny cis - Cis "zmarnowany" na ścieżkę
long long final_cis_hedges = total_cis_available - cis_used_in_path;
// Wyjście
cout << final_hedges_count << " " << final_cis_hedges << "\n";
for (const auto& row : result_map) {
cout << row << "\n";
}
return 0;
}