#include <stdio.h>
// 2062. Up 2
int directions[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int map[21][21] = {0};
int getGroupSize(int y, int x) {
int size = 1;
int groupType = map[y][x];
map[y][x] = -1;
int y2, x2;
for (int i = 0; i < 4; i++) {
y2 = y + directions[i][0];
x2 = x + directions[i][1];
if (map[y2][x2] == groupType) {
size += getGroupSize(y2, x2);
}
}
return size;
}
int main() {
int m, n;
scanf("%d %d", &m, &n);
for (int y = 0; y <= m + 1; y += m + 1) {
for (int x = 1; x <= n; x++) {
map[y][x] = -1;
}
}
for (int x = 0; x <= n + 1; x += n + 1) {
for (int y = 1; y <= m; y++) {
map[y][x] = -1;
}
}
for (int y = 1; y <= m; y++) {
for (int x = 1; x <= n; x++) {
scanf("%d", &map[y][x]);
}
}
int balloons[10] = {0};
int floor, groupSize;
for (int y = 1; y <= m; y++) {
for (int x = 1; x <= n; x++) {
if (map[y][x] == -1) {
continue;
}
floor = map[y][x];
groupSize = getGroupSize(y, x);
if (balloons[floor] < groupSize) {
balloons[floor] = groupSize;
}
}
}
for (int i = 0; i < 10; i++) {
if (balloons[i] > 0) {
printf("%d %d\n", i, balloons[i]);
}
}
return 0;
}
//#include <stdio.h>
//
//typedef struct NodeStruct {
// int id, edgeCount;
// struct NodeStruct* connections[100];
//} Node;
//
//int queue[100] = {0};
//int front = 0;
//int rear = 0;
//
//int queueSize() {
// return (rear + 100 - front) % 100;
//}
//
//int queueEmpty() {
// return rear == front;
//}
//
//void enqueue(int value) {
// queue[rear] = value;
// rear = (rear + 1) % 100;
//}
//
//int dequeue() {
// front = (front + 1) % 100;
// return queue[front - 1];
//}
//
//int main() {
// int n, m, a, b;
// int centerNodeData[100] = {0};
// Node nodes[100];
// for (int i = 0; i < 100; i++) {
// nodes[i].id = i + 1;
// nodes[i].edgeCount = 0;
// }
//
// scanf("%d\n%d", &n, &m);
// for (int i = 0; i < m; i++) {
// scanf("%d %d", &a, &b);
// a--;
// b--;
// nodes[a].connections[nodes[a].edgeCount++] = &nodes[b];
// nodes[b].connections[nodes[b].edgeCount++] = &nodes[a];
// }
//
// int stack[100] = {0};
// int checked[100] = {0};
// int visited[100] = {0};
// int stackSize = 0;
// int centerNode, centerMaxSteps, maxSteps;
// Node stackTopNode, queueFrontNode;
// for (int i = 0; i < 100; i++) {
// if (checked[i]) {
// continue;
// }
// stack[stackSize++] = i;
// while (stackSize > 0) {
// stackTopNode = stack[stackSize - 1];
// for (int j = 0; j < 100; j++) {
// visited[j] = 0;
// }
// enqueue(stackTopNode.id - 1);
// for (maxSteps = 0; !queueEmpty(); maxSteps++) {
// for (int j = queueSize(); j > 0; j--) {
// queueFrontNode = dequeue();
// visited[queueFrontNode.id - 1] = 1;
// for ()
// }
// }
//
// checked[stackTopNode.id - 1] = 1;
// }
// }
//}
//int main() {
// int n, m, a, b, group1, group2;
// int groupCount = 0;
// int groupMembers[101] = {0};
// int assignedGroups[101] = {0};
// int connected[101][101] = {0};
// int maxSteps[101] = {0};
// scanf("%d\n%d", &n, &m);
// for (int i = 0; i < m; i++) {
// scanf("%d %d", &a, &b);
// group1 = assignedGroups[a];
// group2 = assignedGroups[b];
// if (group1 != group2) {
// if (group1 * group2 == 0) {
// assignedGroups[a] = group1 + group2;
// assignedGroups[b] = group1 + group2;
// groupMembers[group1 + group2] += 1;
// } else {
// for (int j = 1; j <= n; j++) {
// if (assignedGroups[j] == group2) {
// assignedGroups[j] = group1;
// groupMembers[group1] += 1;
// }
// }
// }
// } else if (group1 == 0) {
// groupCount += 1;
// assignedGroups[a] = groupCount;
// assignedGroups[b] = groupCount;
// groupMembers[groupCount] = 2;
// }
// connected[a][b] = 1;
// connected[b][a] = 1;
// }
//
// int steps, remainingMembers, currentGroup;
// for (int i = 1; i <= n; i++) {
// currentGroup = assignedGroups[i];
// assignedGroups[i] = 0;
// enqueue(i);
// remainingMembers = groupMembers[currentGroup] - 1;
// for (steps = 1; remainingMembers > 0; steps++) {
// for (int j = 1; j <= 100; j++) {
// if (assignedGroups[j] == )
// }
// }
// }
//}