// General imports import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; //Nearly correct solution...one/two bugs passed most testcases public class Island { static int N, M; static int[][] grid; static boolean[][] visited; static Map islands = new HashMap<>(); public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); M = Integer.parseInt(st.nextToken()); grid = new int[N][M]; visited = new boolean[N][M]; for (int i = 0; i < N; i++) { st = new StringTokenizer(br.readLine()); for (int j = 0; j < M; j++) { int cell = Integer.parseInt(st.nextToken()); grid[i][j] = cell; } } br.close(); for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { if (visited[i][j]) continue; if (grid[i][j] > 0) { islands.put(grid[i][j], islands.getOrDefault(grid[i][j], 0) + 1); search(grid[i][j], i, j); } else { visited[i][j] = true; } } } if (islands.size() == 0) { System.out.println(0); return; } for (Map.Entry e : islands.entrySet()) { int terrainNumber = e.getKey(); int islandNum = e.getValue(); System.out.printf("%d %d%n", terrainNumber, islandNum); } } static void search(int val, int i, int j) { if (visited[i][j]) return; visited[i][j] = true; for (int dir = 0; dir < 4; dir++) { switch (dir) { case 0: if (j + 1 < M && grid[i][j + 1] == val) { search(val, i, j + 1); } break; case 1: if (i + 1 < N && grid[i + 1][j] == val) { search(val, i + 1, j); } break; case 2: if (j - 1 >= 0 && grid[i][j - 1] == val) { search(val, i, j - 1); } break; case 3: if (i - 1 >= 0 && grid[i - 1][j] == val) { search(val, i - 1, j); } break; } } } }