
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <iterator>
#include <map>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>

struct TmieInput {
  uint32_t rows;
  uint32_t columns;
  std::shared_ptr<std::vector<std::vector<uint32_t>>> graph;
};

TmieInput pull_input() {
  uint32_t rows;
  uint32_t columns;
  std::cin >> rows;
  std::cin >> columns;

  std::shared_ptr<std::vector<std::vector<uint32_t>>> graph =
      std::make_shared<std::vector<std::vector<uint32_t>>>(
          rows, std::vector<uint32_t>(columns));
  for (uint32_t i = 0; i < rows; i++) {
    for (uint32_t j = 0; j < columns; j++) {
      (*graph)[i][j] = 0;
      std::cin >> (*graph)[i][j];
    }
  }
  return TmieInput{rows, columns, graph};
}

void clear_island_dfs(uint32_t target, uint32_t r, uint32_t c,
                      std::shared_ptr<std::vector<std::vector<uint32_t>>> data) {
  std::vector<std::pair<uint32_t, uint32_t>> stack = {{r, c}};
  while (!stack.empty()) {
    const auto [current_r, current_c] = stack.back();
    stack.pop_back();

    if (current_r >= data->size() || current_c >= data->at(current_r).size())
      continue;
    if (data->at(current_r)[current_c] != target)
      continue;

    data->at(current_r)[current_c] = 0;
    if (current_r > 0)
      stack.push_back({current_r - 1, current_c});
    if (current_c > 0)
      stack.push_back({current_r, current_c - 1});
    stack.push_back({current_r + 1, current_c});
    stack.push_back({current_r, current_c + 1});
  }
}
int main(int argc, char *argv[]) {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  TmieInput input = pull_input();
  std::map<uint32_t, uint32_t> terrain_counts = {};
  for (uint32_t i = 0; i < input.rows; i++) {
    for (uint32_t j = 0; j < input.columns; j++) {
      const uint32_t terrain_type = input.graph->at(i)[j];
      if (terrain_type == 0)
        continue;

      clear_island_dfs(terrain_type, i, j, input.graph);
      terrain_counts[terrain_type] = terrain_counts[terrain_type] + 1;
    }
  }
  if (terrain_counts.size() == 0)
    std::printf("0");
  for (auto iterator = terrain_counts.cbegin();
       iterator != terrain_counts.cend(); iterator++) {
    std::printf("%d %d \n", iterator->first, iterator->second);
  }
  return 0;
}
