You stumbled upon a new kind of chess puzzles. The chessboard you are given is not necesserily $$$8 \times 8$$$, but it still is $$$N \times N$$$. Each square has some number written on it, all the numbers are from $$$1$$$ to $$$N^2$$$ and all the numbers are pairwise distinct. The $$$j$$$-th square in the $$$i$$$-th row has a number $$$A_{ij}$$$ written on it.In your chess set you have only three pieces: a knight, a bishop and a rook. At first, you put one of them on the square with the number $$$1$$$ (you can choose which one). Then you want to reach square $$$2$$$ (possibly passing through some other squares in process), then square $$$3$$$ and so on until you reach square $$$N^2$$$. In one step you are allowed to either make a valid move with the current piece or replace it with some other piece. Each square can be visited arbitrary number of times.A knight can move to a square that is two squares away horizontally and one square vertically, or two squares vertically and one square horizontally. A bishop moves diagonally. A rook moves horizontally or vertically. The move should be performed to a different square from the one a piece is currently standing on.You want to minimize the number of steps of the whole traversal. Among all the paths to have the same number of steps you want to choose the one with the lowest number of piece replacements.What is the path you should take to satisfy all conditions?

The first line contains a single integer $$$N$$$ ($$$3 \le N \le 10$$$) — the size of the chessboard. Each of the next $$$N$$$ lines contains $$$N$$$ integers $$$A_{i1}, A_{i2}, \dots, A_{iN}$$$ ($$$1 \le A_{ij} \le N^2$$$) — the numbers written on the squares of the $$$i$$$-th row of the board. It is guaranteed that all $$$A_{ij}$$$ are pairwise distinct.

The only line should contain two integers — the number of steps in the best answer and the number of replacement moves in it.

NoteHere are the steps for the first example (the starting piece is a knight):  Move to $$$(3, 2)$$$  Move to $$$(1, 3)$$$  Move to $$$(3, 2)$$$  Replace the knight with a rook  Move to $$$(3, 1)$$$  Move to $$$(3, 3)$$$  Move to $$$(3, 2)$$$  Move to $$$(2, 2)$$$  Move to $$$(2, 3)$$$  Move to $$$(2, 1)$$$  Move to $$$(1, 1)$$$  Move to $$$(1, 2)$$$

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class P1065D {

	static class Node{
		int i, j;
		int minNumber;
		int replacements;
		int currentPiece; //0-knight, 1--rook, 2-bishop
		int dist;

		public Node(int a, int b, int c, int d, int e, int f) {
			i=a;
			j=b;
			minNumber=c;
			replacements=d;
			currentPiece=e;
			dist=f;
		}
	}

	static int n;
	static int[][] g;
	static boolean[][][][][] mark;
	static int[][][][][] dist;

	static int[] kdx = {1, 2, 1, 2, -1, -2, -1, -2};
	static int[] kdy = {2, 1, -2, -1, 2, 1, -2, -1};

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		g = new int[n][n];
		int sx=0, sy=0;
		int ex=0, ey=0;
		for(int i=0; i<n; i++) {
			for(int j=0; j<n; j++) {
				g[i][j] = in.nextInt()-1;
				if (g[i][j]==0) {
					sx=i;
					sy=j;
				} else if (g[i][j]==n*n-1) {
					ex=i;
					ey=j;
				}
			}
		}

		mark = new boolean[n][n][n*n+1][n*n+1][3];
		dist = new int[n][n][n*n+1][n*n+1][3];
		for(int i=0; i<n; i++) {
			for(int j=0; j<n; j++) {
				for(int p=0; p<n*n+1; p++) {
					for(int q=0; q<n*n+1; q++) {
						for(int z=0; z<3; z++) {
							dist[i][j][p][q][z] = (int)1e8;
						}
					}
				}
			}
		}

		Queue<Node> queue = new LinkedList<>();
		dist[sx][sy][0][0][0]=0;
		dist[sx][sy][0][0][1]=0;
		dist[sx][sy][0][0][2]=0;
		queue.add(new Node(sx, sy, 0, 0, 0, 0));
		queue.add(new Node(sx, sy, 0, 0, 1, 0));
		queue.add(new Node(sx, sy, 0, 0, 2, 0));

		while(!queue.isEmpty()) {
//			System.out.println("POP");
			Node node = queue.poll();
			int x = node.i;
			int y = node.j;
			//remember to add the state with replacement

			//PIECE REPLACEMENT
			for(int np=0; np<3; np++) {
				if (np==node.currentPiece) continue;
				int nx=node.i;
				int ny=node.j;
				if (node.replacements==n*n) continue;
				if (mark[nx][ny][node.minNumber][node.replacements+1][np]) continue;
				queue.add(new Node(nx, ny, node.minNumber, node.replacements+1, np, node.dist+1));
				mark[nx][ny][node.minNumber][node.replacements+1][np]=true;
				dist[nx][ny][node.minNumber][node.replacements+1][np]=node.dist+1;
			}

			if (node.currentPiece==0) {
				//KNIGHT
				for(int k=0; k<8; k++) {
					int nx = x+kdx[k];
					int ny = y+kdy[k];
					if (nx<0 || nx>=n || ny<0 || ny>=n) continue;
					int newMinNumber = node.minNumber;
					if (g[nx][ny]==node.minNumber+1) {
						newMinNumber = node.minNumber+1;
					}
					if (mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]) continue;
					queue.add(new Node(nx, ny, newMinNumber, node.replacements, node.currentPiece, node.dist+1));
					mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]=true;
					dist[nx][ny][newMinNumber][node.replacements][node.currentPiece] = node.dist+1;
				}
			} else if (node.currentPiece==1) {
				//ROOK
				for(int i=0; i<n; i++) {
					if (i==node.i) continue;
					//to i, node.y
					int nx = i;
					int ny = node.j;
					int newMinNumber = node.minNumber;
					if (g[nx][ny]==node.minNumber+1) {
						newMinNumber = node.minNumber+1;
					}
					if (mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]) continue;
					queue.add(new Node(nx, ny, newMinNumber, node.replacements, node.currentPiece, node.dist+1));
					mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]=true;
					dist[nx][ny][newMinNumber][node.replacements][node.currentPiece]=node.dist+1;
				}
				for(int j=0; j<n; j++) {
					if (j==node.j) continue;
					//to node.x, j
					int nx = node.i;
					int ny = j;
					int newMinNumber = node.minNumber;
					if (g[nx][ny]==node.minNumber+1) {
						newMinNumber = node.minNumber+1;
					}
					if (mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]) continue;
					queue.add(new Node(nx, ny, newMinNumber, node.replacements, node.currentPiece, node.dist+1));
					mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]=true;
					dist[nx][ny][newMinNumber][node.replacements][node.currentPiece]=node.dist+1;
				}
			} else {
				//BISHOP
				for(int i=node.i, j=node.j; i<n && j<n; i++, j++) {
					if (i==node.i && j==node.j) continue;
					int nx = i;
					int ny = j;
					int newMinNumber = node.minNumber;
					if (g[nx][ny]==node.minNumber+1) {
						newMinNumber = node.minNumber+1;
					}
					if (mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]) continue;
					queue.add(new Node(nx, ny, newMinNumber, node.replacements, node.currentPiece, node.dist+1));
					mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]=true;
					dist[nx][ny][newMinNumber][node.replacements][node.currentPiece] = node.dist+1;
				}
				for(int i=node.i, j=node.j; i<n && j>=0; i++, j--) {
					if (i==node.i && j==node.j) continue;
					int nx = i;
					int ny = j;
					int newMinNumber = node.minNumber;
					if (g[nx][ny]==node.minNumber+1) {
						newMinNumber = node.minNumber+1;
					}
					if (mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]) continue;
					queue.add(new Node(nx, ny, newMinNumber, node.replacements, node.currentPiece, node.dist+1));
					mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]=true;
					dist[nx][ny][newMinNumber][node.replacements][node.currentPiece] = node.dist+1;
				}
				for(int i=node.i, j=node.j; i>=0 && j<n; i--, j++) {
					if (i==node.i && j==node.j) continue;
					int nx = i;
					int ny = j;
					int newMinNumber = node.minNumber;
					if (g[nx][ny]==node.minNumber+1) {
						newMinNumber = node.minNumber+1;
					}
					if (mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]) continue;
					queue.add(new Node(nx, ny, newMinNumber, node.replacements, node.currentPiece, node.dist+1));
					mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]=true;
					dist[nx][ny][newMinNumber][node.replacements][node.currentPiece] = node.dist+1;
				}
				for(int i=node.i, j=node.j; i>=0 && j>=0; i--, j--) {
					if (i==node.i && j==node.j) continue;
					int nx = i;
					int ny = j;
					int newMinNumber = node.minNumber;
					if (g[nx][ny]==node.minNumber+1) {
						newMinNumber = node.minNumber+1;
					}
					if (mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]) continue;
					queue.add(new Node(nx, ny, newMinNumber, node.replacements, node.currentPiece, node.dist+1));
					mark[nx][ny][newMinNumber][node.replacements][node.currentPiece]=true;
					dist[nx][ny][newMinNumber][node.replacements][node.currentPiece] = node.dist+1;
				}
			}
		}

		long ans=(long)1e15;
		int minrep = (int)1e8;
		for(int replacement=0; replacement<=n*n; replacement++) {
			for(int cp=0; cp<3; cp++) {
				if(ans>dist[ex][ey][n*n-1][replacement][cp]) {
					minrep=replacement;
					ans = dist[ex][ey][n*n-1][replacement][cp];
				}
			}

		}
		System.out.println(ans+" "+minrep);

	}

}

import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.StringTokenizer;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.util.ArrayList;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        ThreePieces solver = new ThreePieces();
        solver.solve(1, in, out);
        out.close();
    }

    static class ThreePieces {
        int[][][][][] dp = new int[201][10][10][100][3];
        int INF = (int) (1e9);
        ArrayList<int[]>[][][] move = new ArrayList[10][10][3];
        int N;
        int[] moveR = new int[]{-2, -1, 1, 2};
        int[] moveC = new int[]{-2, -1, 1, 2};

        public void solve(int testNumber, InputReader in, PrintWriter out) {
            N = in.nextInt();
            int[][] loc = new int[N * N][2];
            int[][] arr = new int[N][N];
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    int a = in.nextInt() - 1;
                    loc[a] = new int[]{i, j};
                    arr[i][j] = a;
                    move[i][j][0] = posKnight(i, j);
                    move[i][j][1] = posBishop(i, j);
                    move[i][j][2] = posRook(i, j);
                }
            }
            for (int i = 0; i <= 200; i++) {
                for (int j = 0; j < 10; j++) {
                    for (int k = 0; k < 10; k++) {
                        for (int d = 0; d < 100; d++) {
                            for (int z = 0; z < 3; z++) {
                                dp[i][j][k][d][z] = INF;
                            }
                        }
                    }
                }
            }
            Arrays.fill(dp[0][loc[0][0]][loc[0][1]][0], 0);
            for (int i = 0; i < 200; i++) {
                for (int j = 0; j < N; j++) {
                    for (int k = 0; k < N; k++) {
                        for (int d = 0; d < N * N; d++) {
                            for (int m = 0; m < 3; m++) {
                                for (int next = 0; next < 3; next++) {
                                    if (next == m) {
                                        for (int[] c : move[j][k][next]) {
                                            if (arr[c[0]][c[1]] == d + 1) {
                                                dp[i + 1][c[0]][c[1]][d + 1][next] = Math.min(dp[i + 1][c[0]][c[1]][d + 1][next], dp[i][j][k][d][m]);
                                            } else {
                                                dp[i + 1][c[0]][c[1]][d][next] = Math.min(dp[i + 1][c[0]][c[1]][d][next], dp[i][j][k][d][m]);
                                            }
                                        }
                                    } else {
                                        dp[i + 1][j][k][d][next] = Math.min(dp[i + 1][j][k][d][next], dp[i][j][k][d][m] + 1);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            for (int i = 2; i <= 200; i++) {
                if (has(dp[i][loc[N * N - 1][0]][loc[N * N - 1][1]][N * N - 1])) {
                    int res = INF;
                    for (int v : dp[i][loc[N * N - 1][0]][loc[N * N - 1][1]][N * N - 1]) {
                        res = Math.min(res, v);
                    }
                    out.println(i + " " + res);
                    return;
                }
            }
        }

        boolean has(int[] a) {
            for (int i : a) {
                if (i != INF) {
                    return true;
                }
            }
            return false;
        }

        boolean isValid(int r, int c) {
            return r >= 0 && r < N && c >= 0 && c < N;
        }

        ArrayList<int[]> posKnight(int r, int c) {
            ArrayList<int[]> pos = new ArrayList<>();
            for (int rM : moveR) {
                for (int cM : moveC) {
                    if (Math.abs(rM) != Math.abs(cM)) {
                        int newR = r + rM;
                        int newC = c + cM;
                        if (isValid(newR, newC)) {
                            pos.add(new int[]{newR, newC});
                        }
                    }
                }
            }
            return pos;
        }

        ArrayList<int[]> posRook(int r, int c) {
            ArrayList<int[]> pos = new ArrayList<>();
            for (int i = 0; i < N; i++) {
                pos.add(new int[]{r, i});
                pos.add(new int[]{i, c});
            }
            return pos;
        }

        ArrayList<int[]> posBishop(int r, int c) {
            ArrayList<int[]> pos = new ArrayList<>();
            for (int k = 1; k < N; k++) {
                int[] mR = new int[]{k, -k, k, -k};
                int[] mC = new int[]{k, k, -k, -k};
                for (int i = 0; i < 4; i++) {
                    int newR = r + mR[i];
                    int newC = c + mC[i];
                    if (isValid(newR, newC)) {
                        pos.add(new int[]{newR, newC});
                    }
                }
            }
            return pos;
        }

    }

    static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

    }
}

import java.util.PriorityQueue;
import java.util.Scanner;

public class Main {

	private static class Element implements Comparable<Element> {
		public int x;
		public int y;
		public int nextValue;
		public int type;
		public int changeTime;
		public int dist;
		public Element(int x , int y , int nextValue , int type , int changeTime , int dist) {
			this.x = x;
			this.y = y;
			this.nextValue = nextValue;
			this.type = type;
			this.changeTime = changeTime;
			this.dist = dist;
		}
		@Override
		public int compareTo(Element e) {
			if (this.dist < e.dist) {
				return - 1;
			} else if (this.dist > e.dist) {
				return 1;
			} else {
				if (this.changeTime < e.changeTime) {
					return - 1;
				} else if (this.changeTime > e.changeTime) {
					return 1;
				} else {
					return 0;
				}
			}
		}
	}

	private static final int BISHOP = 0 , KNIGHT = 1 , ROOK = 2;

	private static int[][][][] dists = new int[20][20][110][3];
	private static int[][][][] changes = new int[20][20][110][3];
	private static int[][] values = new int[100][100];

	public static void main(String[] args) {

		Scanner scan = new Scanner(System.in);

		int i , j , k , n = scan.nextInt() , x = - 1 , y = - 1;
		for (i = 0;i < n;i ++) {
			for (j = 0;j < n;j ++) {
				values[i][j] = scan.nextInt();
				if (values[i][j] == 1) {
					x = i;
					y = j;
				}
			}
		}
		for (i = 0;i < n;i ++) {
			for (j = 0;j < n;j ++) {
				for (k = 1;k <= 100;k ++) {
					dists[i][j][k][0] = dists[i][j][k][1] = dists[i][j][k][2] = - 1;
				}
			}
		}
		dists[x][y][1][BISHOP] = 0;
		dists[x][y][1][KNIGHT] = 0;
		dists[x][y][1][ROOK] = 0;

		PriorityQueue<Element> queue = new PriorityQueue<>();
		queue.add(new Element(x , y , 1 , BISHOP , 0 , 0));
		queue.add(new Element(x , y , 1 , KNIGHT , 0 , 0));
		queue.add(new Element(x , y , 1 , ROOK , 0 , 0));

		while (!queue.isEmpty()) {
			Element e = queue.poll();
			if (dists[e.x][e.y][e.nextValue][e.type] == e.dist) {
				int nextValue = e.nextValue;
				if (e.nextValue == values[e.x][e.y]) {
					if (e.nextValue == n * n) {
						System.out.println(e.dist + " " + e.changeTime);
						break;
					} else {
						nextValue ++;
					}
				}
				for (i = 0;i < 3;i ++) {
					int nextChangeTime = e.changeTime;
					int nextType = i;
					int nextDist = e.dist + 1;
					if (i != e.type) {
						nextChangeTime ++;
						nextDist ++;
					}
					if (i == BISHOP) {
						for (int dx = - 1;dx <= 1;dx += 2) {
							for (int dy = - 1;dy <= 1;dy += 2) {
								int tx = e.x + dx , ty = e.y + dy;
								while (tx >= 0 && tx < n && ty >= 0 && ty < n) {
									if (nextDist < dists[tx][ty][nextValue][nextType] || dists[tx][ty][nextValue][nextType] < 0) {
										dists[tx][ty][nextValue][nextType] = nextDist;
										changes[tx][ty][nextValue][nextType] = nextChangeTime;
										queue.add(new Element(tx , ty , nextValue , nextType, nextChangeTime , nextDist));
									} else if (nextDist == dists[tx][ty][nextValue][nextType]) {
										if (nextChangeTime < changes[tx][ty][nextValue][nextType]) {
											changes[tx][ty][nextValue][nextType] = nextChangeTime;
											queue.add(new Element(tx , ty , nextValue , nextType, nextChangeTime , nextDist));
										}
									}
									tx += dx;
									ty += dy;
								}
							}
						}
					} else if (i == KNIGHT) {
						for (int d = 0;d < 8;d ++) {
							int tx = e.x + knightDx[d];
							int ty = e.y + knightDy[d];
							if (tx >= 0 && tx < n && ty >= 0 && ty < n) {
								if (nextDist < dists[tx][ty][nextValue][nextType] || dists[tx][ty][nextValue][nextType] < 0) {
									dists[tx][ty][nextValue][nextType] = nextDist;
									changes[tx][ty][nextValue][nextType] = nextChangeTime;
									queue.add(new Element(tx , ty , nextValue , nextType, nextChangeTime , nextDist));
								} else if (nextDist == dists[tx][ty][nextValue][nextType]) {
									if (nextChangeTime < changes[tx][ty][nextValue][nextType]) {
										changes[tx][ty][nextValue][nextType] = nextChangeTime;
										queue.add(new Element(tx , ty , nextValue , nextType, nextChangeTime , nextDist));
									}
								}
							}
						}
					} else {
						// ROOK
						int tx = e.x , ty = e.y - 1;
						// left
						while (tx >= 0 && tx < n && ty >= 0 && ty < n) {
							if (nextDist < dists[tx][ty][nextValue][nextType] || dists[tx][ty][nextValue][nextType] < 0) {
								dists[tx][ty][nextValue][nextType] = nextDist;
								changes[tx][ty][nextValue][nextType] = nextChangeTime;
								queue.add(new Element(tx , ty , nextValue , nextType, nextChangeTime , nextDist));
							} else if (nextDist == dists[tx][ty][nextValue][nextType]) {
								if (nextChangeTime < changes[tx][ty][nextValue][nextType]) {
									changes[tx][ty][nextValue][nextType] = nextChangeTime;
									queue.add(new Element(tx , ty , nextValue , nextType, nextChangeTime , nextDist));
								}
							}
							ty --;
						}
						// right
						tx = e.x;
						ty = e.y + 1;
						while (tx >= 0 && tx < n && ty >= 0 && ty < n) {
							if (nextDist < dists[tx][ty][nextValue][nextType] || dists[tx][ty][nextValue][nextType] < 0) {
								dists[tx][ty][nextValue][nextType] = nextDist;
								changes[tx][ty][nextValue][nextType] = nextChangeTime;
								queue.add(new Element(tx , ty , nextValue , nextType, nextChangeTime , nextDist));
							} else if (nextDist == dists[tx][ty][nextValue][nextType]) {
								if (nextChangeTime < changes[tx][ty][nextValue][nextType]) {
									changes[tx][ty][nextValue][nextType] = nextChangeTime;
									queue.add(new Element(tx , ty , nextValue , nextType, nextChangeTime , nextDist));
								}
							}
							ty ++;
						}
						// up
						tx = e.x - 1;
						ty = e.y;
						while (tx >= 0 && tx < n && ty >= 0 && ty < n) {
							if (nextDist < dists[tx][ty][nextValue][nextType] || dists[tx][ty][nextValue][nextType] < 0) {
								dists[tx][ty][nextValue][nextType] = nextDist;
								changes[tx][ty][nextValue][nextType] = nextChangeTime;
								queue.add(new Element(tx , ty , nextValue , nextType, nextChangeTime , nextDist));
							} else if (nextDist == dists[tx][ty][nextValue][nextType]) {
								if (nextChangeTime < changes[tx][ty][nextValue][nextType]) {
									changes[tx][ty][nextValue][nextType] = nextChangeTime;
									queue.add(new Element(tx , ty , nextValue , nextType, nextChangeTime , nextDist));
								}
							}
							tx --;
						}
						// down
						tx = e.x + 1;
						ty = e.y;
						while (tx >= 0 && tx < n && ty >= 0 && ty < n) {
							if (nextDist < dists[tx][ty][nextValue][nextType] || dists[tx][ty][nextValue][nextType] < 0) {
								dists[tx][ty][nextValue][nextType] = nextDist;
								changes[tx][ty][nextValue][nextType] = nextChangeTime;
								queue.add(new Element(tx , ty , nextValue , nextType, nextChangeTime , nextDist));
							} else if (nextDist == dists[tx][ty][nextValue][nextType]) {
								if (nextChangeTime < changes[tx][ty][nextValue][nextType]) {
									changes[tx][ty][nextValue][nextType] = nextChangeTime;
									queue.add(new Element(tx , ty , nextValue , nextType, nextChangeTime , nextDist));
								}
							}
							tx ++;
						}
					}
				}
			}
		}

	}

	private static int[] knightDx = new int[] {- 2 , - 1 , 1 , 2 , 2 , 1 , - 1 , - 2};
	private static int[] knightDy = new int[] {1 , 2 , 2 , 1 , - 1 , - 2 , - 2 , - 1};

}

import java.io.*;
import java.util.*;

public class CF1065D {
	static final int INF = Integer.MAX_VALUE, K = 0, B = 1, R = 2;
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int n = Integer.parseInt(br.readLine());
		int[] ij = new int[n * n];
		for (int i = 0; i < n; i++) {
			StringTokenizer st = new StringTokenizer(br.readLine());
			for (int j = 0; j < n; j++) {
				int a = Integer.parseInt(st.nextToken()) - 1;
				ij[a] = i * n + j;
			}
		}
		int[][][][][][] ss = new int[n][n][n][n][3][3];
		int[][][][][][] rr = new int[n][n][n][n][3][3];
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++)
				for (int i_ = 0; i_ < n; i_++)
					for (int j_ = 0; j_ < n; j_++)
						for (int u = 0; u < 3; u++)
							for (int v = 0; v < 3; v++)
								ss[i][j][i_][j_][u][v] = INF;
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++)
				for (int i_ = 0; i_ < n; i_++)
					for (int j_ = 0; j_ < n; j_++)
						if (i == i_ && j == j_)
							for (int u = 0; u < 3; u++)
								for (int v = 0; v < 3; v++)
									ss[i][j][i][j][u][v] = rr[i][j][i][j][u][v] = u == v ? 0 : 1;
						else {
							int di = Math.abs(i - i_);
							int dj = Math.abs(j - j_);
							if (di == 2 && dj == 1 || di == 1 && dj == 2)
								ss[i][j][i_][j_][K][K] = 1;
							if (di == 0 || dj == 0)
								ss[i][j][i_][j_][R][R] = 1;
							if (i + j == i_ + j_ || i - j == i_ - j_)
								ss[i][j][i_][j_][B][B] = 1;
						}
		for (int z = 0; z < n * n; z++) {
			int iz = z / n, jz = z % n;
			for (int w = 0; w < 3; w++)
				for (int x = 0; x < n * n; x++) {
					int ix = x / n, jx = x % n;
					for (int u = 0; u < 3; u++) {
						int sxz = ss[ix][jx][iz][jz][u][w];
						int rxz = rr[ix][jx][iz][jz][u][w];
						if (sxz == INF)
							continue;
						for (int y = 0; y < n * n; y++) {
							int iy = y / n, jy = y % n;
							for (int v = 0; v < 3; v++) {
								int szy = ss[iz][jz][iy][jy][w][v];
								int rzy = rr[iz][jz][iy][jy][w][v];
								if (szy == INF)
									continue;
								int sxy = ss[ix][jx][iy][jy][u][v];
								int rxy = rr[ix][jx][iy][jy][u][v];
								if (sxy > sxz + szy || sxy == sxz + szy && rxy > rxz + rzy) {
									ss[ix][jx][iy][jy][u][v] = sxz + szy;
									rr[ix][jx][iy][jy][u][v] = rxz + rzy;
								}
							}
						}
					}
				}
		}
		int[] ps = new int[3], pr = new int[3];
		int[] qs = new int[3], qr = new int[3], tmp;
		int r, s;
		for (int h = 1; h < n * n; h++) {
			int i = ij[h - 1] / n, j = ij[h - 1] % n, i_ = ij[h] / n, j_ = ij[h] % n;
			for (int v = 0; v < 3; v++) {
				s = INF; r = 0;
				for (int u = 0; u < 3; u++) {
					int s0 = ps[u], s1 = ss[i][j][i_][j_][u][v];
					int s_ = s0 == INF || s1 == INF ? INF : s0 + s1;
					int r_ = pr[u] + rr[i][j][i_][j_][u][v];
					if (s > s_ || s == s_ && r > r_) {
						s = s_; r = r_;
					}
				}
				qs[v] = s; qr[v] = r;
			}
			tmp = ps; ps = qs; qs = tmp;
			tmp = pr; pr = qr; qr = tmp;
		}
		s = INF; r = 0;
		for (int v = 0; v < 3; v++) {
			int s_ = ps[v], r_ = pr[v];
			if (s > s_ || s == s_ && r > r_) {
				s = s_; r = r_;
			}
		}
		System.out.println(s + " " + r);
	}
}

import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author Vadim
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        TaskD solver = new TaskD();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskD {
        public static final int MAX_PATH = 1000000;
        public static final int K = 0;
        public static final int B = 1;
        public static final int R = 2;
        Point[] coords;
        int[][] d;
        int[] di = new int[]{2, 2, 1, -1, -2, -2, 1, -1};
        int[] dj = new int[]{-1, 1, 2, 2, 1, -1, -2, -2};
        int n;

        int getBDist(int r, int c, int ra, int ca) {
            if (r == ra && c == ca)
                return 0;
            if ((r + c) % 2 != (ra + ca) % 2)
                return MAX_PATH;
            if (r - ra == c - ca || r - ra == ca - c)
                return 1;
            return 2;
        }

        int getRDist(int r, int c, int ra, int ca) {
            if (r == ra && c == ca)
                return 0;
            if (r == ra || c == ca)
                return 1;
            return 2;
        }

        Pair dist(int from, int to, int start, int end) {
            Point p = coords[from];
            Point q = coords[to];
            if (start == end) {
                if (start == K)
                    return new Pair(d[from][to], 0);
                else if (start == B) {
                    return new Pair(getBDist(p.r, p.c, q.r, q.c), 0);//todo:
                } else
                    return new Pair(getRDist(p.r, p.c, q.r, q.c), 0);
            } else {
                if (start == R && end == B || start == B && end == R) {
                    if (getBDist(p.r, p.c, q.r, q.c) == 1)
                        return new Pair(2, 1);
                    if (getRDist(p.r, p.c, q.r, q.c) == 1)
                        return new Pair(2, 1);
                    return new Pair(3, 1);
                }
                if (start == K && end == B || start == B && end == K) {
                    if (end == K) {
                        Point t = p;
                        p = q;
                        q = t;
                    }
                    int min = getBDist(p.r, p.c, q.r, q.c);
                    for (int i = 0; i < 8; i++) {
                        int ni = p.r + di[i];
                        int nj = p.c + dj[i];
                        if (0 <= ni && ni < n && 0 <= nj && nj < n) {
                            min = Math.min(1 + getBDist(ni, nj, q.r, q.c), min);
                        }
                    }
                    return new Pair(min + 1, 1);
                }
                if (start == K && end == R || start == R && end == K) {
                    if (end == K) {
                        Point t = p;
                        p = q;
                        q = t;
                    }
                    int min = getRDist(p.r, p.c, q.r, q.c);
                    for (int i = 0; i < 8; i++) {
                        int ni = p.r + di[i];
                        int nj = p.c + dj[i];
                        if (0 <= ni && ni < n && 0 <= nj && nj < n) {
                            min = Math.min(1 + getRDist(ni, nj, q.r, q.c), min);
                        }
                    }
                    return new Pair(min + 1, 1);
                }
            }
            throw new RuntimeException();

        }

        public void solve(int testNumber, InputReader in, PrintWriter out) {

            n = in.ni();
            int[][] a = new int[n][n];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    a[i][j] = in.ni() - 1;
                }
            }
            int n2 = n * n;

            d = new int[n2][n2];
            for (int i = 0; i < n2; i++) {
                for (int j = 0; j < n2; j++) {
                    if (i == j)
                        d[i][i] = 0;
                    else
                        d[i][j] = MAX_PATH;
                }
            }

            coords = new Point[n2];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    coords[a[i][j]] = new Point(i, j);
                    for (int k = 0; k < 8; k++) {
                        int ni = i + di[k];
                        int nj = j + dj[k];
                        if (0 <= ni && ni < n && 0 <= nj && nj < n) {
                            d[a[i][j]][a[ni][nj]] = 1;
                        }
                    }

                }
            }
            for (int k = 0; k < n2; k++) {
                for (int i = 0; i < n2; i++) {
                    for (int j = 0; j < n2; j++) {
                        d[i][j] = Math.min(d[i][j], d[i][k] + d[k][j]);
                    }
                }
            }
            int ans[][] = new int[n2][3];
            int swaps[][] = new int[n2][3];
            for (int i = 0; i < n2 - 1; i++) {
                for (int j = 0; j < 3; j++) {
                    int minDist = MAX_PATH;
                    int minSwap = n2;
                    for (int k = 0; k < 3; k++) {
                        Pair pair = dist(i, i + 1, k, j);
                        int dist = ans[i][k] + pair.x;
                        int swap = swaps[i][k] + pair.y;
                        if (dist < minDist || dist == minDist && swap < minSwap) {
                            minDist = dist;
                            minSwap = swap;
                        }
                    }
                    ans[i + 1][j] += minDist;
                    swaps[i + 1][j] = minSwap;
                }
            }
            int minDist = MAX_PATH;
            int minSwap = n2;
            for (int k = 0; k < 3; k++) {
                int dist = ans[n2 - 1][k];
                int swap = swaps[n2 - 1][k];
                if (dist < minDist || dist == minDist && swap < minSwap) {
                    minDist = dist;
                    minSwap = swap;
                }
            }

            out.println(minDist + " " + minSwap);

        }

        class Point {
            int r;
            int c;

            public Point(int r, int c) {
                this.r = r;
                this.c = c;
            }

        }

        class Pair {
            int x;
            int y;

            public Pair(int x, int y) {
                this.x = x;
                this.y = y;
            }

        }

    }

    static class InputReader extends BufferedReader {
        public InputReader(InputStream st) {
            super(new InputStreamReader(st));
        }

        public String readLine() {
            try {
                return super.readLine();
            } catch (IOException e) {
                return null;
            }
        }

        private int readByte() {
            try {
                return read();
            } catch (IOException e) {
                throw new RuntimeException();
            }
        }

        public int ni() {
            int num = 0, b;
            boolean minus = false;
            while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-')) ;
            if (b == '-') {
                minus = true;
                b = readByte();
            }

            while (true) {
                if (b >= '0' && b <= '9') {
                    num = num * 10 + (b - '0');
                } else {
                    return minus ? -num : num;
                }
                b = readByte();
            }
        }

    }
}

import java.io.*;
import java.util.*;

public class D {

    static int add=(int)1e5;

    public static void main(String[] args) throws Throwable {
        MyScanner sc = new MyScanner();
        PrintWriter pw = new PrintWriter(System.out);

        n=sc.nextInt();
        int [][] a=new int [n][n];
        int [] x=new int [n*n+1];
        int [] y=new int [n*n+1];
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++){
                a[i][j]=sc.nextInt();
                x[a[i][j]]=i;
                y[a[i][j]]=j;
            }
        dis=new long [n*n+1][3][3];
        for(int i=1;i<n*n;i++){
            for(int p=0;p<3;p++){
                long [][] tmpDis=new long [n*n+1][3];
                for(long [] xx : tmpDis)
                    Arrays.fill(xx,(long)2e18);
                Queue<pair> q=new LinkedList<>();
                q.add(new pair(x[i],y[i],p));
                tmpDis[i][p]=0;
                while(!q.isEmpty()){
                    pair cur=q.poll();
                    int num=a[cur.x][cur.y];
                    for(int j=0;j<3;j++)
                        if(j!=cur.p && tmpDis[num][j]>tmpDis[num][cur.p]+add+1){
                            tmpDis[num][j]=tmpDis[num][cur.p]+add+1;
                            q.add(new pair(cur.x,cur.y,j));
                        }
                    if(cur.p==0){ // knight
                        for(int j=0;j<8;j++){
                            int x2=cur.x+dx[j];
                            int y2=cur.y+dy[j];
                            if(valid(x2,y2) && tmpDis[a[x2][y2]][0]>tmpDis[num][0]+add){
                                tmpDis[a[x2][y2]][0]=tmpDis[num][0]+add;
                                q.add(new pair(x2,y2,0));
                            }
                        }
                    }else
                        if(cur.p==1){ // rock
                            for(int j=0;j<n;j++){
                                if(j==cur.x)
                                    continue;
                                int x2=j;
                                int y2=cur.y;
                                if(valid(x2,y2) && tmpDis[a[x2][y2]][1]>tmpDis[num][1]+add){
                                    tmpDis[a[x2][y2]][1]=tmpDis[num][1]+add;
                                    q.add(new pair(x2,y2,1));
                                }
                            }
                            for(int j=0;j<n;j++){
                                if(j==cur.y)
                                    continue;
                                int x2=cur.x;
                                int y2=j;
                                if(valid(x2,y2) && tmpDis[a[x2][y2]][1]>tmpDis[num][1]+add){
                                    tmpDis[a[x2][y2]][1]=tmpDis[num][1]+add;
                                    q.add(new pair(x2,y2,1));
                                }
                            }
                        }else{
                            for(int j=1;j<10;j++){
                                int x2=cur.x+j;
                                int y2=cur.y+j;
                                if(valid(x2,y2) && tmpDis[a[x2][y2]][2]>tmpDis[num][2]+add){
                                    tmpDis[a[x2][y2]][2]=tmpDis[num][2]+add;
                                    q.add(new pair(x2,y2,2));
                                }

                                x2=cur.x+j;
                                y2=cur.y-j;
                                if(valid(x2,y2) && tmpDis[a[x2][y2]][2]>tmpDis[num][2]+add){
                                    tmpDis[a[x2][y2]][2]=tmpDis[num][2]+add;
                                    q.add(new pair(x2,y2,2));
                                }

                                x2=cur.x-j;
                                y2=cur.y-j;
                                if(valid(x2,y2) && tmpDis[a[x2][y2]][2]>tmpDis[num][2]+add){
                                    tmpDis[a[x2][y2]][2]=tmpDis[num][2]+add;
                                    q.add(new pair(x2,y2,2));
                                }

                                x2=cur.x-j;
                                y2=cur.y+j;
                                if(valid(x2,y2) && tmpDis[a[x2][y2]][2]>tmpDis[num][2]+add){
                                    tmpDis[a[x2][y2]][2]=tmpDis[num][2]+add;
                                    q.add(new pair(x2,y2,2));
                                }

                            }
                        }

                }
                for(int p2=0;p2<3;p2++)
                    dis[i+1][p][p2]=tmpDis[i+1][p2];
            }
        }

        mem=new Long[n*n+1][4];
        long ans=Math.min(dp(1,0),Math.min(dp(1,1),dp(1,2)));
        pw.println(ans/add+" "+ans%add);

        pw.flush();
        pw.close();
    }

    static Long [][] mem;

    static long dp(int i,int p){
        if(i==n*n)
            return 0;
        if(mem[i][p]!=null)
            return mem[i][p];
        long ret=(long)2e18;
        for(int p2=0;p2<3;p2++)
            ret=Math.min(ret,dis[i+1][p][p2]+dp(i+1,p2));
        return mem[i][p]=ret;
    }

    static long [][][] dis;

    static int [] dx={1,1,2,2,-1,-1,-2,-2};
    static int [] dy={2,-2,1,-1,2,-2,1,-1};

    static int n;
    static boolean valid(int i,int j){
        return Math.min(i,j)>=0 && Math.max(i,j)<n;
    }

    static class pair{
        int x,y,p;
        pair(int a,int b,int c){
            x=a;
            y=b;
            p=c;
        }
    }

    static class MyScanner {
        BufferedReader br;
        StringTokenizer st;

        public MyScanner() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        String nextLine() {
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
    }
}

import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author dmytro.prytula
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        DTriFiguri solver = new DTriFiguri();
        solver.solve(1, in, out);
        out.close();
    }

    static class DTriFiguri {
        static int N;
        int M = 305;
        int INF = (int) 1e9;
        DTriFiguri.Point[] possibleKnight = new DTriFiguri.Point[]{
                DTriFiguri.Point.of(2, 1),
                DTriFiguri.Point.of(-2, 1),
                DTriFiguri.Point.of(2, -1),
                DTriFiguri.Point.of(-2, -1),
                DTriFiguri.Point.of(1, 2),
                DTriFiguri.Point.of(-1, 2),
                DTriFiguri.Point.of(1, -2),
                DTriFiguri.Point.of(-1, -2)
        };
        DTriFiguri.Point[][] dist = new DTriFiguri.Point[M][M];

        {

        }

        private boolean in(DTriFiguri.Point p) {
            return 0 <= p.x && p.x < N && 0 <= p.y && p.y < N;
        }

        public void solve(int testNumber, InputReader in, PrintWriter out) {

            N = in.nextInt();
            DTriFiguri.Point[] pos = new DTriFiguri.Point[N * N];
            int[][] A = new int[N][N];
            for (int x = 0; x < N; x++) {
                for (int y = 0; y < N; y++) {
                    A[x][y] = in.nextInt() - 1;
                    pos[A[x][y]] = DTriFiguri.Point.of(x, y);

                }
            }

            for (int i = 0; i < M; i++) {
                for (int j = 0; j < M; j++) {
//                Arrays.fill(dist[i], Point.of(INF, INF));
                    dist[i][j] = DTriFiguri.Point.of(INF, INF);
                }
            }
            for (int i = 0; i < M; i++)
//            for (int j = 0; j < M; j++)
//                if (i == j)
                dist[i][i] = DTriFiguri.Point.of(0, 0);

            for (int x = 0; x < N; x++) {
                for (int y = 0; y < N; y++) {
                    //knight
                    for (int i = 0; i < possibleKnight.length; i++) {
                        int nx = x + possibleKnight[i].x;
                        int ny = y + possibleKnight[i].y;
                        if (in(DTriFiguri.Point.of(nx, ny))) {
                            dist[DTriFiguri.Point.hash(x, y, 0)][DTriFiguri.Point.hash(nx, ny, 0)] = DTriFiguri.Point.of(1, 0);
                        }
                    }
                    //bishop
                    for (int i = -N + 1; i <= N - 1; i++) {
                        int nx = x + i;
                        int ny = y + i;
                        if (in(DTriFiguri.Point.of(nx, ny)))
                            dist[DTriFiguri.Point.hash(x, y, 1)][DTriFiguri.Point.hash(nx, ny, 1)] = DTriFiguri.Point.of(1, 0);

                        ny = y - i;
                        if (in(DTriFiguri.Point.of(nx, ny)))
                            dist[DTriFiguri.Point.hash(x, y, 1)][DTriFiguri.Point.hash(nx, ny, 1)] = DTriFiguri.Point.of(1, 0);
                    }

                    //rook
                    for (int i = 0; i < N; i++) {
                        int nx = i;
                        int ny = y;
//                    if (in(Point.of(nx, ny)))
                        dist[DTriFiguri.Point.hash(x, y, 2)][DTriFiguri.Point.hash(nx, ny, 2)] = DTriFiguri.Point.of(1, 0);
                        nx = x;
                        ny = i;
//                    if (in(Point.of(nx, ny)))
                        dist[DTriFiguri.Point.hash(x, y, 2)][DTriFiguri.Point.hash(nx, ny, 2)] = DTriFiguri.Point.of(1, 0);

                    }

                    for (int i = 0; i < 3; i++) {
                        for (int j = 0; j < 3; j++) {
                            if (i != j) {
                                dist[DTriFiguri.Point.hash(x, y, i)][DTriFiguri.Point.hash(x, y, j)] = DTriFiguri.Point.of(1, 1);
                            }
                        }
                    }

                }
            }

            for (int k = 0; k < M; k++) {
                for (int i = 0; i < M; i++) {
                    for (int j = 0; j < M; j++) {
                        dist[i][j] = DTriFiguri.Point.min(dist[i][j], DTriFiguri.Point.sum(dist[i][k], dist[k][j]));
                    }
                }
            }

            DTriFiguri.Point[][] dp = new DTriFiguri.Point[N * N][3];
            for (int i = 0; i < N * N; i++) {
                for (int j = 0; j < 3; j++) {
                    dp[i][j] = DTriFiguri.Point.of(INF, INF);
                }
            }

            dp[0][0] = dp[0][1] = dp[0][2] = DTriFiguri.Point.of(0, 0);

            for (int i = 0; i < N * N - 1; i++)
                for (int j = 0; j < 3; j++)
                    for (int k = 0; k < 3; k++)
                        dp[i + 1][k] = DTriFiguri.Point.min(
                                dp[i + 1][k],
                                DTriFiguri.Point.sum(dp[i][j], dist[DTriFiguri.Point.hash(pos[i].x, pos[i].y, j)][DTriFiguri.Point.hash(pos[i + 1].x, pos[i + 1].y, k)])
                        );

            DTriFiguri.Point ans = DTriFiguri.Point.of(INF, INF);
            ans = DTriFiguri.Point.min(ans, dp[N * N - 1][0]);
            ans = DTriFiguri.Point.min(ans, dp[N * N - 1][1]);
            ans = DTriFiguri.Point.min(ans, dp[N * N - 1][2]);

            out.print(ans.toString());
        }

        private static class Point {
            int x;
            int y;

            private Point(int x, int y) {
                this.x = x;
                this.y = y;
            }

            public static DTriFiguri.Point of(int x, int y) {
                return new DTriFiguri.Point(x, y);
            }

            public static int hash(int x, int y, int p) {
                return x * N * 3 + y * 3 + p;
            }

            public static DTriFiguri.Point min(DTriFiguri.Point a, DTriFiguri.Point b) {
                if (a.x == b.x) {
                    return a.y < b.y ? a : b;
                }
                return a.x < b.x ? a : b;
            }

            public static DTriFiguri.Point sum(DTriFiguri.Point point1, DTriFiguri.Point point) {
                return DTriFiguri.Point.of(point1.x + point.x, point1.y + point.y);
            }

            public String toString() {
                return x + " " + y;
            }

        }

    }

    static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String nextToken() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(nextToken());
        }

    }
}

import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author dmytro.prytula
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        DTriFiguri solver = new DTriFiguri();
        solver.solve(1, in, out);
        out.close();
    }

    static class DTriFiguri {
        static int N;
        int M = 305;
        int INF = (int) 1e9;
        DTriFiguri.Point[] possibleKnight = new DTriFiguri.Point[]{
                DTriFiguri.Point.of(2, 1),
                DTriFiguri.Point.of(-2, 1),
                DTriFiguri.Point.of(2, -1),
                DTriFiguri.Point.of(-2, -1),
                DTriFiguri.Point.of(1, 2),
                DTriFiguri.Point.of(-1, 2),
                DTriFiguri.Point.of(1, -2),
                DTriFiguri.Point.of(-1, -2)
        };
        DTriFiguri.Point[][] dist = new DTriFiguri.Point[M][M];

        {

        }

        private boolean in(DTriFiguri.Point p) {
            return 0 <= p.x && p.x < N && 0 <= p.y && p.y < N;
        }

        public void solve(int testNumber, InputReader in, PrintWriter out) {

            N = in.nextInt();
            DTriFiguri.Point[] pos = new DTriFiguri.Point[N * N];
            int[][] A = new int[N][N];
            for (int x = 0; x < N; x++) {
                for (int y = 0; y < N; y++) {
                    A[x][y] = in.nextInt() - 1;
                    pos[A[x][y]] = DTriFiguri.Point.of(x, y);

                }
            }

            for (int i = 0; i < M; i++) {
                for (int j = 0; j < M; j++) {
//                Arrays.fill(dist[i], Point.of(INF, INF));
                    dist[i][j] = DTriFiguri.Point.of(INF, INF);
                }
            }
            for (int i = 0; i < M; i++)
//            for (int j = 0; j < M; j++)
//                if (i == j)
                dist[i][i] = DTriFiguri.Point.of(0, 0);

            for (int x = 0; x < N; x++) {
                for (int y = 0; y < N; y++) {
                    //knight
                    for (int i = 0; i < possibleKnight.length; i++) {
                        int nx = x + possibleKnight[i].x;
                        int ny = y + possibleKnight[i].y;
                        if (in(DTriFiguri.Point.of(nx, ny))) {
                            dist[DTriFiguri.Point.hash(x, y, 0)][DTriFiguri.Point.hash(nx, ny, 0)] = DTriFiguri.Point.of(1, 0);
                        }
                    }
                    //bishop
                    for (int i = -N + 1; i <= N - 1; i++) {
                        int nx = x + i;
                        int ny = y + i;
                        if (in(DTriFiguri.Point.of(nx, ny)))
                            dist[DTriFiguri.Point.hash(x, y, 1)][DTriFiguri.Point.hash(nx, ny, 1)] = DTriFiguri.Point.of(1, 0);

                        ny = y - i;
                        if (in(DTriFiguri.Point.of(nx, ny)))
                            dist[DTriFiguri.Point.hash(x, y, 1)][DTriFiguri.Point.hash(nx, ny, 1)] = DTriFiguri.Point.of(1, 0);
                    }

                    //rook
                    for (int i = 0; i < N; i++) {
                        int nx = i;
                        int ny = y;
//                    if (in(Point.of(nx, ny)))
                        dist[DTriFiguri.Point.hash(x, y, 2)][DTriFiguri.Point.hash(nx, ny, 2)] = DTriFiguri.Point.of(1, 0);
                        nx = x;
                        ny = i;
//                    if (in(Point.of(nx, ny)))
                        dist[DTriFiguri.Point.hash(x, y, 2)][DTriFiguri.Point.hash(nx, ny, 2)] = DTriFiguri.Point.of(1, 0);

                    }

                    for (int i = 0; i < 3; i++) {
                        for (int j = 0; j < 3; j++) {
                            if (i != j) {
                                dist[DTriFiguri.Point.hash(x, y, i)][DTriFiguri.Point.hash(x, y, j)] = DTriFiguri.Point.of(1, 1);
                            }
                        }
                    }

                }
            }

            for (int k = 0; k < M; k++) {
                for (int i = 0; i < M; i++) {
                    for (int j = 0; j < M; j++) {
                        dist[i][j] = DTriFiguri.Point.min(dist[i][j], DTriFiguri.Point.sum(dist[i][k], dist[k][j]));
                    }
                }
            }

            DTriFiguri.Point[][] dp = new DTriFiguri.Point[N * N][3];
            for (int i = 0; i < N * N; i++) {
                for (int j = 0; j < 3; j++) {
                    dp[i][j] = DTriFiguri.Point.of(INF, INF);
                }
            }

            dp[0][0] = dp[0][1] = dp[0][2] = DTriFiguri.Point.of(0, 0);

            for (int i = 0; i < N * N - 1; i++)
                for (int j = 0; j < 3; j++)
                    for (int k = 0; k < 3; k++)
                        dp[i + 1][k] = DTriFiguri.Point.min(
                                dp[i + 1][k],
                                DTriFiguri.Point.sum(dp[i][j], dist[DTriFiguri.Point.hash(pos[i].x, pos[i].y, j)][DTriFiguri.Point.hash(pos[i + 1].x, pos[i + 1].y, k)])
                        );

            DTriFiguri.Point ans = DTriFiguri.Point.of(INF, INF);
            ans = DTriFiguri.Point.min(ans, dp[N * N - 1][0]);
            ans = DTriFiguri.Point.min(ans, dp[N * N - 1][1]);
            ans = DTriFiguri.Point.min(ans, dp[N * N - 1][2]);

            out.print(ans.toString());
        }

        private static class Point {
            int x;
            int y;

            private Point(int x, int y) {
                this.x = x;
                this.y = y;
            }

            public static DTriFiguri.Point of(int x, int y) {
                return new DTriFiguri.Point(x, y);
            }

            public static int hash(int x, int y, int p) {
                return x * 10 * 3 + y * 3 + p;
            }

            public static DTriFiguri.Point min(DTriFiguri.Point a, DTriFiguri.Point b) {
                if (a.x == b.x) {
                    return a.y < b.y ? a : b;
                }
                return a.x < b.x ? a : b;
            }

            public static DTriFiguri.Point sum(DTriFiguri.Point point1, DTriFiguri.Point point) {
                return DTriFiguri.Point.of(point1.x + point.x, point1.y + point.y);
            }

            public String toString() {
                return x + " " + y;
            }

        }

    }

    static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String nextToken() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(nextToken());
        }

    }
}

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.*;

public class d{

	static class Pos{
		int i,j;
		Pos(int i, int j){this.i=i;this.j=j;}
	}
	static class Node{
		int i,j,k,l,c;
		Node(int i,int j,int k,int l,int c){this.i=i;this.j=j;this.k=k;this.l=l;this.c=c;}
	}

	static void solve(){
		int n = ni();
		int[][][][] dis = new int[n][n][3][n*n+1];
		Pos[] pos =new Pos[n*n];
		for(int i=0;i<n;++i)for(int j=0;j<n;++j)pos[ni()-1] = new Pos(i, j);
		for(int i=0;i<n;++i)for(int j=0;j<n;++j)for(int k=0;k<3;++k)for(int l=0;l<=n*n;++l)dis[i][j][k][l]=Integer.MAX_VALUE;
		PriorityQueue<Node> que = new PriorityQueue<>((a,b)->a.c-b.c);
		for(int k=0;k<3;++k){
			dis[pos[0].i][pos[0].j][k][1]=0;
			que.add(new Node(pos[0].i, pos[0].j, k, 1, 0));
		}
		List<Integer> movex[] = new List[3];
		List<Integer> movey[] = new List[3];
		movex[0] = Arrays.asList(-2,-2,-1,-1,1,1,2,2);
		movey[0] = Arrays.asList(1,-1,2,-2,2,-2,1,-1);
		for(int i=1;i<3;++i){
			movex[i]=new ArrayList<>();
			movey[i]=new ArrayList<>();
		}
		for(int i=1;i<=n;++i){
			movex[1].add(i);movex[1].add(-i);
			movey[1].add(0);movey[1].add(0);
			movex[1].add(0);movex[1].add(0);
			movey[1].add(i);movey[1].add(-i);

			movex[2].add(i);movex[2].add(-i);
			movey[2].add(i);movey[2].add(-i);
			movex[2].add(i);movex[2].add(-i);
			movey[2].add(-i);movey[2].add(i);
		}
		while(!que.isEmpty()){
			Node node = que.poll();
			if(node.c > dis[node.i][node.j][node.k][node.l])continue;
			int i=node.i, j=node.j, k=node.k, l=node.l;
			if(l==n*n){
				out.println(node.c/1000+" "+node.c%1000);
				return;
			}
			for(int kk=0;kk<3;++kk){
				if(kk==k)continue;
				if(dis[i][j][kk][l] > dis[i][j][k][l]+1001){
					dis[i][j][kk][l]=dis[i][j][k][l]+1001;
					que.add(new Node(i, j, kk, l, dis[i][j][kk][l]));
				}
			}
			for(int x=0;x<movex[k].size();++x){
				int dx = movex[k].get(x);
				int dy = movey[k].get(x);

				if(inrange(i+dy, j+dx, n, n)){
					int ll = l;
					if(pos[l].i==i+dy && pos[l].j==j+dx)++ll;
					if(dis[i+dy][j+dx][k][ll]>dis[i][j][k][l]+1000){
						dis[i+dy][j+dx][k][ll]=dis[i][j][k][l]+1000;
						que.add(new Node(i+dy, j+dx, k, ll, dis[i+dy][j+dx][k][ll]));
					}
				}
			}
		}

	}

	 public static void main(String[] args){
		 solve();
		 out.flush();
	 }
	 private static InputStream in = System.in;
	 private static PrintWriter out = new PrintWriter(System.out);

	 static boolean inrange(int y, int x, int h, int w){
		 return y>=0 && y<h && x>=0 && x<w;
	 }
	 @SuppressWarnings("unchecked")
	 static<T extends Comparable> int lower_bound(List<T> list, T key){
		 int lower=-1;int upper=list.size();
		 while(upper - lower>1){
		 int center =(upper+lower)/2;
		 if(list.get(center).compareTo(key)>=0)upper=center;
		 else lower=center;
		 }
		 return upper;
	 }
	 @SuppressWarnings("unchecked")
	 static <T extends Comparable> int upper_bound(List<T> list, T key){
		 int lower=-1;int upper=list.size();
		 while(upper-lower >1){
		 int center=(upper+lower)/2;
		 if(list.get(center).compareTo(key)>0)upper=center;
		 else lower=center;
		 }
		 return upper;
	 }
	 @SuppressWarnings("unchecked")
	 static <T extends Comparable> boolean next_permutation(List<T> list){
		 int lastIndex = list.size()-2;
		 while(lastIndex>=0 && list.get(lastIndex).compareTo(list.get(lastIndex+1))>=0)--lastIndex;
		 if(lastIndex<0)return false;
		 int swapIndex = list.size()-1;
		 while(list.get(lastIndex).compareTo(list.get(swapIndex))>=0)swapIndex--;
		 T tmp = list.get(lastIndex);
		 list.set(lastIndex++, list.get(swapIndex));
		 list.set(swapIndex, tmp);
		 swapIndex = list.size()-1;
		 while(lastIndex<swapIndex){
		 tmp = list.get(lastIndex);
		 list.set(lastIndex, list.get(swapIndex));
		 list.set(swapIndex, tmp);
		 ++lastIndex;--swapIndex;
		 }
		 return true;
	 }
	 private static final byte[] buffer = new byte[1<<15];
	 private static int ptr = 0;
	 private static int buflen = 0;
	 private static boolean hasNextByte(){
		 if(ptr<buflen)return true;
		 ptr = 0;
		 try{
			 buflen = in.read(buffer);
		 } catch (IOException e){
			 e.printStackTrace();
		 }
		 return buflen>0;
	 }
	 private static int readByte(){ if(hasNextByte()) return buffer[ptr++]; else return -1;}
	 private static boolean isSpaceChar(int c){ return !(33<=c && c<=126);}
	 private static int skip(){int res; while((res=readByte())!=-1 && isSpaceChar(res)); return res;}

	 private static double nd(){ return Double.parseDouble(ns()); }
	 private static char nc(){ return (char)skip(); }
	 private static String ns(){
		 StringBuilder sb = new StringBuilder();
		 for(int b=skip();!isSpaceChar(b);b=readByte())sb.append((char)b);
		 return sb.toString();
	 }
	 private static int[] nia(int n){
		 int[] res = new int[n];
		 for(int i=0;i<n;++i)res[i]=ni();
		 return res;
	 }
	 private static long[] nla(int n){
		 long[] res = new long[n];
		 for(int i=0;i<n;++i)res[i]=nl();
		 return res;
	 }
	 private static int ni(){
		 int res=0,b;
		 boolean minus=false;
		 while((b=readByte())!=-1 && !((b>='0'&&b<='9') || b=='-'));
		 if(b=='-'){
			 minus=true;
			 b=readByte();
		 }
		 for(;'0'<=b&&b<='9';b=readByte())res=res*10+(b-'0');
		 return minus ? -res:res;
	 }
	 private static long nl(){
		 long res=0,b;
		 boolean minus=false;
		 while((b=readByte())!=-1 && !((b>='0'&&b<='9') || b=='-'));
		 if(b=='-'){
			 minus=true;
			 b=readByte();
		 }
		 for(;'0'<=b&&b<='9';b=readByte())res=res*10+(b-'0');
		 return minus ? -res:res;
	}
}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

public class Main {

    static int n;
    static int[] x;
    static int[] y;

    // 0代表马, 1代表象, 2代表车
    public static void main(String[] args) throws IOException {

        StreamTokenizer streamTokenizer = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        streamTokenizer.nextToken();
        n = (int) streamTokenizer.nval;
        x = new int[n * n + 1];
        y = new int[n * n + 1];
        int[][] f = new int[3][n * n + 1];
        int[][] g = new int[3][n * n + 1];
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++) {
                streamTokenizer.nextToken();
                x[(int) streamTokenizer.nval] = i;
                y[(int) streamTokenizer.nval] = j;
            }
        f[0][1] = 0;
        f[1][1] = 0;
        f[2][1] = 0;
        g[0][1] = 0;
        g[1][1] = 0;
        g[2][1] = 0;
        for (int i = 2; i <= n * n; i++) {
            for (int j = 0; j < 3; j++) {
                getMinStep(x[i - 1], y[i - 1], 0, x[i], y[i], j);
                f[j][i] = f[0][i - 1] + minStep;
                g[j][i] = g[0][i - 1] + minMove;
                getMinStep(x[i - 1], y[i - 1], 1, x[i], y[i], j);
                if ((f[1][i - 1] + minStep < f[j][i]) || ((f[1][i - 1] + minStep == f[j][i]) && (g[1][i - 1] + minMove < g[j][i]))) {
                    f[j][i] = f[1][i - 1] + minStep;
                    g[j][i] = g[1][i - 1] + minMove;
                }
                getMinStep(x[i - 1], y[i - 1], 2, x[i], y[i], j);
                if ((f[2][i - 1] + minStep < f[j][i]) || ((f[2][i - 1] + minStep == f[j][i]) && (g[2][i - 1] + minMove < g[j][i]))) {
                    f[j][i] = f[2][i - 1] + minStep;
                    g[j][i] = g[2][i - 1] + minMove;
                }
            }
        }
        minStep = f[0][n * n];
        minMove = g[0][n * n];

        if ((f[1][n * n] < minStep) || ((f[1][n * n] == minStep) && (g[1][n * n] < minMove))) {
            minStep = f[1][n * n];
            minMove = g[1][n * n];
        }
        if ((f[2][n * n] < minStep) || ((f[2][n * n] == minStep) && (g[2][n * n] < minMove))) {
            minStep = f[2][n * n];
            minMove = g[2][n * n];
        }
        System.out.print(minStep + " " + minMove);
    }

    static int[] pointX = new int[1000000];
    static int[] pointY = new int[1000000];
    static int[] state = new int[1000000];
    static int[] steps = new int[1000000];
    static int[] moves = new int[1000000];

    static int[][] knight = {
            {-2, -1},
            {-2, 1},
            {-1, -2},
            {-1, 2},
            {1, -2},
            {1, 2},
            {2, -1},
            {2, 1}
    };

    static int minStep;
    static int minMove;

    // 0代表马, 1代表象, 2代表车
    public static void getMinStep(int fromX, int fromY, int fromState, int toX, int toY, int toState) {

        int l = 0, r = 0;
        int tempX, tempY;
        pointX[0] = fromX;
        pointY[0] = fromY;
        state[0] = fromState;
        steps[0] = 0;
        moves[0] = 0;
        while (l <= r) {
            if (state[l] == 0) {
                for (int i = 0; i < knight.length; i++) {
                    tempX = pointX[l] + knight[i][0];
                    tempY = pointY[l] + knight[i][1];
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 0;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 0 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    }
                }

                r++;
                pointX[r] = pointX[l];
                pointY[r] = pointY[l];
                state[r] = 1;
                steps[r] = steps[l] + 1;
                moves[r] = moves[l] + 1;
                if (pointX[r] == toX && pointY[r] == toY && 1 == toState) {
                    minStep = steps[r];
                    minMove = moves[r];
                    return;
                }

                r++;
                pointX[r] = pointX[l];
                pointY[r] = pointY[l];
                state[r] = 2;
                steps[r] = steps[l] + 1;
                moves[r] = moves[l] + 1;
                if (pointX[r] == toX && pointY[r] == toY && 2 == toState) {
                    minStep = steps[r];
                    minMove = moves[r];
                    return;
                }
            } else if (state[l] == 1) {
                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempX += 1;
                    tempY += 1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 1;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 1 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempX += 1;
                    tempY += -1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 1;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 1 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempX += -1;
                    tempY += 1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 1;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 1 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempX += -1;
                    tempY += -1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 1;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 1 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                r++;
                pointX[r] = pointX[l];
                pointY[r] = pointY[l];
                state[r] = 0;
                steps[r] = steps[l] + 1;
                moves[r] = moves[l] + 1;
                if (pointX[r] == toX && pointY[r] == toY && 0 == toState) {
                    minStep = steps[r];
                    minMove = moves[r];
                    return;
                }

                r++;
                pointX[r] = pointX[l];
                pointY[r] = pointY[l];
                state[r] = 2;
                steps[r] = steps[l] + 1;
                moves[r] = moves[l] + 1;
                if (pointX[r] == toX && pointY[r] == toY && 2 == toState) {
                    minStep = steps[r];
                    minMove = moves[r];
                    return;
                }

            } else {
                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempX += 1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 2;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 2 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempX += -1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 2;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 2 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempY += 1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 2;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 2 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempY += -1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 2;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 2 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                r++;
                pointX[r] = pointX[l];
                pointY[r] = pointY[l];
                state[r] = 0;
                steps[r] = steps[l] + 1;
                moves[r] = moves[l] + 1;
                if (pointX[r] == toX && pointY[r] == toY && 0 == toState) {
                    minStep = steps[r];
                    minMove = moves[r];
                    return;
                }

                r++;
                pointX[r] = pointX[l];
                pointY[r] = pointY[l];
                state[r] = 1;
                steps[r] = steps[l] + 1;
                moves[r] = moves[l] + 1;
                if (pointX[r] == toX && pointY[r] == toY && 1 == toState) {
                    minStep = steps[r];
                    minMove = moves[r];
                    return;
                }
            }
            l++;
        }
    }

    public static boolean isInMatrix(int x, int y) {
        if (x >= 1 && x <= n && y >= 1 && y <= n)
            return true;
        return false;
    }

}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class D2 {
    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        String nextLine() {
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
    }
    public static FastReader scn = new FastReader();

    // type 0 knight 1 bishop 2 rook
    private static class node implements Comparable<node>{
        int x;
        int y;
        int val;
        int steps;
        int rep;
        int type;

        public node(int x,int y,int val, int st,int rep, int typ){
            this.x = x;
            this.y = y;
            this.rep = rep;
            this.val = val;
            this.steps = st;
            this.type = typ;
        }

        @Override
        public int compareTo(node other){
            if(this.steps == other.steps) return this.rep - other.rep;
            return this.steps - other.steps;
        }
    }

    public static void main(String[] args) {
        int n = scn.nextInt();
        int[][] pos = new int[n*n +1][2] ;
        for(int i = 0; i<n; i++){
            for(int j = 0; j<n; j++){
                pos[scn.nextInt()] = new int[]{i,j};
            }
        }

        PriorityQueue<node> Queue = new PriorityQueue<>();
        Queue.add(new node(pos[1][0],pos[1][1],1,0,0,0));
        Queue.add(new node(pos[1][0],pos[1][1],1,0,0,1));
        Queue.add(new node(pos[1][0],pos[1][1],1,0,0,2));

        int[][][] move = new int[3][][];
        move[0] = new int[][]{{-2,-1},{-1,-2},{2,-1},{1,-2},{-2,1},{-1,2},{2,1},{1,2}};
        move[1] = new int[][]{{-1,-1},{-1,1},{1,-1},{1,1}};
        move[2] = new int[][]{{-1,0},{1,0},{0,-1},{0,1}};

        boolean[][][][] visited = new boolean[n][n][n*n +1][3]; // x,y,value,type

        while(!Queue.isEmpty()){
            node s = Queue.poll();
            if(s.x<0 || s.y<0 || s.x >=n || s.y >=n) continue;
            if(visited[s.x][s.y][s.val][s.type]) continue;
            visited[s.x][s.y][s.val][s.type] = true;
            if(pos[s.val+1][0] == s.x && pos[s.val+1][1] == s.y){
                s.val++;
                if(s.val == n*n){
                    System.out.println(s.steps + " " + s.rep);
                    break;
                }
                visited[s.x][s.y][s.val][s.type] = true;
            }

            for(int i = 0; i< 3; i++) Queue.add(new node(s.x,s.y,s.val,s.steps+1,s.rep+1,i));

            for(int[] m : move[s.type]){
                if(s.type == 0){
                    Queue.add(new node(s.x + m[0], s.y + m[1], s.val , s.steps+1, s.rep , s.type));
                }else{
                    for(int j = 1; j<=n; j++) Queue.add(new node(s.x + j*m[0], s.y + j*m[1], s.val , s.steps+1, s.rep , s.type));
                }
            }
        }
    }
}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class D2 {
    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        String nextLine() {
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
    }
    public static FastReader scn = new FastReader();

    // type 0 knight 1 bishop 2 rook
    private static class node implements Comparable<node>{
        int x;
        int y;
        int val;
        int steps;
        int rep;
        int type;

        public node(int x,int y,int val, int st,int rep, int typ){
            this.x = x;
            this.y = y;
            this.rep = rep;
            this.val = val;
            this.steps = st;
            this.type = typ;
        }

        @Override
        public int compareTo(node other){
            if(this.steps == other.steps) return this.rep - other.rep;
            return this.steps - other.steps;
        }
    }

    public static void main(String[] args) {
        int n = scn.nextInt();
        int[][] pos = new int[n*n +1][2] ;
        for(int i = 0; i<n; i++){
            for(int j = 0; j<n; j++){
                pos[scn.nextInt()] = new int[]{i,j};
            }
        }

        PriorityQueue<node> Queue = new PriorityQueue<>();
        Queue.add(new node(pos[1][0],pos[1][1],1,0,0,0));
        Queue.add(new node(pos[1][0],pos[1][1],1,0,0,1));
        Queue.add(new node(pos[1][0],pos[1][1],1,0,0,2));

        int[][][] move = new int[3][][];
        move[0] = new int[][]{{-2,-1},{-1,-2},{2,-1},{1,-2},{-2,1},{-1,2},{2,1},{1,2}};
        move[1] = new int[][]{{-1,-1},{-1,1},{1,-1},{1,1}};
        move[2] = new int[][]{{-1,0},{1,0},{0,-1},{0,1}};

        boolean[][][][] visited = new boolean[n][n][n*n +1][3]; // x,y,value,type

        while(!Queue.isEmpty()){
            node s = Queue.poll();
            if(s.x<0 || s.y<0 || s.x >=n || s.y >=n) continue;
            if(visited[s.x][s.y][s.val][s.type]) continue;
            visited[s.x][s.y][s.val][s.type] = true;
            if(pos[s.val+1][0] == s.x && pos[s.val+1][1] == s.y){
                s.val++;
                if(s.val == n*n){
                    System.out.println(s.steps + " " + s.rep);
                    break;
                }
                visited[s.x][s.y][s.val][s.type] = true;
            }

            for(int i = 0; i< 3; i++) Queue.add(new node(s.x,s.y,s.val,s.steps+1,s.rep+1,i));

            for(int[] m : move[s.type]){
                if(s.type == 0){
                    for(int j = 0; j<m.length ; j++) Queue.add(new node(s.x + m[0], s.y + m[1], s.val , s.steps+1, s.rep , s.type));
                }else{
                    for(int j = 1; j<=n; j++) Queue.add(new node(s.x + j*m[0], s.y + j*m[1], s.val , s.steps+1, s.rep , s.type));
                }
            }
        }
    }
}

import java.awt.event.MouseAdapter;
import java.io.*;
import java.lang.reflect.Field;
import java.util.*;

public class Main {

    public static void main(String[] args) throws IOException {
        Reader.init(System.in);
        new Main();
    }

    Main() throws IOException {
        solve();
    }

    void solve() throws IOException {
        PrintWriter out = new PrintWriter(System.out);

        int N = Reader.nextInt();

        PriorityQueue<Node> P = new PriorityQueue<>(new Comparator<Node>() {
            @Override
            public int compare(Node o1, Node o2) {
                if(o1.st == o2.st){return o1.rep - o2.rep;}
                return o1.st - o2.st;
            }
        });

        int[][][][] steps = new int[N+1][N+1][N*N + 1][3];  // [x][y][val][type]  type - 0(bishop) 1(rook) 2(knight)
        int[][][][] reps = new int[N+1][N+1][N*N+1][3];
        boolean[][][][] vis = new boolean[N+1][N+1][N*N+1][3];

        int[][][] move = new int[3][][];
        move[0] = new int[][]{{1,1}, {-1,-1}, {1,-1}, {-1,1}};
        move[1] = new int[][]{{0,1}, {0,-1}, {1,0}, {-1,0}};
        move[2] = new int[][]{{1,2}, {2,1}, {-1,-2}, {-2,-1}, {-2,1}, {1,-2}, {-1,2}, {2,-1}};

        int[][] pos = new int[N*N+1][2];
        for(int i = 1; i <= N; i++){
            for(int j = 1; j <= N; j++){
                int v = Reader.nextInt();
                pos[v] = new int[]{i,j};
            }
        }

        P.add(new Node(1,pos[1][0], pos[1][1],0, 0,0));
        P.add(new Node(1,pos[1][0], pos[1][1],0, 0,1));
        P.add(new Node(1,pos[1][0], pos[1][1],0, 0,2));

        while(!P.isEmpty()){
             Node S = P.poll();
             if(S.x <= 0 || S.x > N || S.y <= 0 || S.y > N){continue;}

             if(vis[S.x][S.y][S.val][S.type]) continue;

             steps[S.x][S.y][S.val][S.type] = S.st;
             reps[S.x][S.y][S.val][S.type] = S.rep;
             vis[S.x][S.y][S.val][S.type] =  true;

            if(S.x == pos[S.val+1][0] && S.y == pos[S.val+1][1]){
                S.val++;
                if(S.val == N*N){out.println(S.st + " " + S.rep); out.close(); return;}
                steps[S.x][S.y][S.val][S.type] = S.st;
                reps[S.x][S.y][S.val][S.type] = S.rep;
                vis[S.x][S.y][S.val][S.type] =  true;
            }

            // replacement
            for(int i = 0; i < 3; i++){
                P.add(new Node(S.val,S.x,S.y,S.rep + 1, S.st+1, i));
            }

            //move
            for(int[] m : move[S.type]){
                if(S.type == 2){
                    for(int i = 0; i < m.length; i++){
                        P.add(new Node(S.val,S.x+m[0],S.y+m[1],S.rep, S.st+1, S.type));
                    }
                }
                else{
                    for(int i = 1; i <= N; i++){
                        P.add(new Node(S.val,S.x+m[0]*i,S.y+m[1]*i,S.rep, S.st+1, S.type));
                    }
                }
            }
        }

        out.close();
    }

    class Node {

        int val;
        int x;
        int y;
        int rep; //count for replacements
        int st; //steps
        int type;

        Node(int val, int x, int y, int rep, int st, int type){
            this.val = val;
            this.x = x;
            this.y = y;
            this.rep = rep;
            this.st = st;
            this.type = type;
        }

    }

}

class Reader {
    static BufferedReader reader;
    static StringTokenizer tokenizer;

    static void init(InputStream input) {
        reader = new BufferedReader(new InputStreamReader(input) );
        tokenizer = new StringTokenizer("");

    }

    static String nextToken() {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            try {

                tokenizer = new StringTokenizer(reader.readLine());

            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return tokenizer.nextToken();
    }

    static String next() throws IOException {

        while ( ! tokenizer.hasMoreTokens() ) {

            tokenizer = new StringTokenizer(

                    reader.readLine() );

        }
        return tokenizer.nextToken();

    }

    static int nextInt() throws IOException {

        return Integer.parseInt( next() );

    }

    static double nextDouble() throws IOException {

        return Double.parseDouble( next() );

    }

    static long nextLong() {

        return Long.parseLong(nextToken());
    }
}

//package baobab;

import java.io.*;
import java.util.*;

public class D {

    public static void main(String[] args) {
        Solver solver = new Solver();
    }

    static class Solver {
        IO io;

        public Solver() {
            this.io = new IO();
            try {
                solve();
            } catch (RuntimeException e) {
                if (!e.getMessage().equals("Clean exit")) {
                    throw e;
                }
            } finally {
                io.close();
            }
        }

        /****************************** START READING HERE ********************************/

        void solve() {
            int n = io.nextInt();
            int[][] b = new int[n][n];
            for (int y=0; y<n; y++) {
                for (int x=0; x<n; x++) {
                    b[y][x] = io.nextInt();
                }
            }
            int end = n*n;
            int[] x = new int[end+1];
            int[] y = new int[end+1];
            for (int i=0; i<n; i++) {
                for (int j=0; j<n; j++) {
                    int v = b[i][j];
                    y[v] = i;
                    x[v] = j;
                }
            }

            State ans = null;
            State[][][][][] leastSteps = new State[n][n][n][n][3];
            Deque<State> q = new ArrayDeque<>();
            q.add(new State(y[1], x[1], y[2], x[2], ROOK, 0, 0));
            q.add(new State(y[1], x[1], y[2], x[2], KNIGHT, 0, 0));
            q.add(new State(y[1], x[1], y[2], x[2], BISHOP, 0, 0));
            while (!q.isEmpty()) {
                State s = q.pollFirst();
                if (s.currY < 0 || s.currX < 0 || s.currY >= n || s.currX >= n) continue;
                State prev = leastSteps[s.currY][s.currX][s.goalY][s.goalX][s.piece];
                if (prev != null) {
                    if (prev.steps < s.steps) continue;
                    if (prev.steps == s.steps && prev.replacements <= s.replacements) continue;
                }
                leastSteps[s.currY][s.currX][s.goalY][s.goalX][s.piece] = s;

                // intermediary goal reached?
                if (s.currX == s.goalX && s.currY == s.goalY) {

                    if (b[s.currY][s.currX] == end) {
                        // actually reached end state (not necessarily with fewest steps because different piece = different state)
                        if (ans == null) ans = s; // first time reaching end state
                        else if (s.steps < ans.steps) ans = s; // reaching end state with this piece is faster than reaching end state with some other piece
                        else if (s.steps == ans.steps && s.replacements < ans.replacements) ans = s; // so far lowest steps & lowest replacements for that step size
                        continue; // stop bfs but dont break because we may find new solution with same steps but fewer replacements
                    }

                    // change state to set next goal.
                    int prevGoal = b[s.goalY][s.goalX];
                    int newGoal = prevGoal + 1;
                    int newGoalX = x[newGoal];
                    int newGoalY = y[newGoal];
                    // front of the queue!
                    q.addFirst(new State(s.goalY, s.goalX, newGoalY, newGoalX, s.piece, s.steps, s.replacements));
                    continue;
                }

                // change piece?
                q.addLast(new State(s.currY, s.currX, s.goalY, s.goalX, ROOK, s.steps+1, s.replacements+1));
                q.addLast(new State(s.currY, s.currX, s.goalY, s.goalX, BISHOP, s.steps+1, s.replacements+1));
                q.addLast(new State(s.currY, s.currX, s.goalY, s.goalX, KNIGHT, s.steps+1, s.replacements+1));

                // move current piece?
                if (s.piece == ROOK) {
                    for (int newY=0; newY<n; newY++) q.addLast(new State(newY, s.currX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    for (int newX=0; newX<n; newX++) q.addLast(new State(s.currY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                } else if (s.piece == KNIGHT) {
                    int newY = s.currY + 1;
                    int newX = s.currX + 2;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY + 1;
                    newX = s.currX - 2;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY - 1;
                    newX = s.currX - 2;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY - 1;
                    newX = s.currX + 2;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY + 2;
                    newX = s.currX + 1;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY - 2;
                    newX = s.currX + 1;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY + 2;
                    newX = s.currX - 1;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY - 2;
                    newX = s.currX - 1;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));

                } else if (s.piece == BISHOP) {
                    int newY = s.currY;
                    int newX = s.currX;
                    while (true) {
                        newY += 1;
                        newX += 1;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY;
                    newX = s.currX;
                    while (true) {
                        newY += 1;
                        newX -= 1;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY;
                    newX = s.currX;
                    while (true) {
                        newY -= 1;
                        newX -= 1;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY;
                    newX = s.currX;
                    while (true) {
                        newY -= 1;
                        newX += 1;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                }
            }

            io.println(ans.steps + " " + ans.replacements);
        }

        static int ROOK = 0;
        static int KNIGHT = 1;
        static int BISHOP = 2;

        class State {
            int currY;
            int currX;
            int goalY;
            int goalX;
            int piece;
            int steps;
            int replacements;

            public State(int currY, int currX, int goalY, int goalX, int piece, int steps, int replacements) {
                this.currY = currY;
                this.currX = currX;
                this.goalY = goalY;
                this.goalX = goalX;
                this.piece = piece;
                this.steps = steps;
                this.replacements = replacements;
            }

            @Override
            public boolean equals(Object o) {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;
                State state = (State) o;
                return currY == state.currY &&
                        currX == state.currX &&
                        goalY == state.goalY &&
                        goalX == state.goalX &&
                        piece == state.piece;
            }

            @Override
            public int hashCode() {
                return Objects.hash(currY, currX, goalY, goalX, piece);
            }
        }

        /************************** UTILITY CODE BELOW THIS LINE **************************/

        long MOD = (long)1e9 + 7;

        boolean closeToZero(double v) {
            // Check if double is close to zero, considering precision issues.
            return Math.abs(v) <= 0.0000000001;
        }

        class DrawGrid {

            void draw(boolean[][] d) {
                System.out.print("  ");
                for (int x=0; x<d[0].length; x++) {
                    System.out.print(" " + x + " ");
                }
                System.out.println("");
                for (int y=0; y<d.length; y++) {
                    System.out.print(y + " ");
                    for (int x=0; x<d[0].length; x++) {
                        System.out.print((d[y][x] ? "[x]" : "[ ]"));
                    }
                    System.out.println("");
                }
            }

            void draw(int[][] d) {
                int max = 1;
                for (int y=0; y<d.length; y++) {
                    for (int x=0; x<d[0].length; x++) {
                        max = Math.max(max, ("" + d[y][x]).length());
                    }
                }
                System.out.print("  ");
                String format = "%" + (max+2) + "s";
                for (int x=0; x<d[0].length; x++) {
                    System.out.print(String.format(format, x) + " ");
                }
                format = "%" + (max) + "s";
                System.out.println("");
                for (int y=0; y<d.length; y++) {
                    System.out.print(y + " ");
                    for (int x=0; x<d[0].length; x++) {
                        System.out.print(" [" + String.format(format, (d[y][x])) + "]");
                    }
                    System.out.println("");
                }
            }

        }

        class IDval implements Comparable<IDval> {
            int steps;
            long replacements;

            public IDval(int steps, long replacements) {
                this.replacements = replacements;
                this.steps = steps;
            }

            @Override
            public int compareTo(IDval o) {
                if (this.replacements < o.replacements) return -1;
                if (this.replacements > o.replacements) return 1;
                return this.steps - o.steps;
            }
        }

        private class ElementCounter {
            private HashMap<Long, Integer> elements;

            public ElementCounter() {
                elements = new HashMap<>();
            }

            public void add(long element) {
                int count = 1;
                Integer prev = elements.get(element);
                if (prev != null) count += prev;
                elements.put(element, count);
            }

            public void remove(long element) {
                int count = elements.remove(element);
                count--;
                if (count > 0) elements.put(element, count);
            }

            public int get(long element) {
                Integer val = elements.get(element);
                if (val == null) return 0;
                return val;
            }

            public int size() {
                return elements.size();
            }
        }

        class StringCounter {
            HashMap<String, Integer> elements;

            public StringCounter() {
                elements = new HashMap<>();
            }

            public void add(String identifier) {
                int count = 1;
                Integer prev = elements.get(identifier);
                if (prev != null) count += prev;
                elements.put(identifier, count);
            }

            public void remove(String identifier) {
                int count = elements.remove(identifier);
                count--;
                if (count > 0) elements.put(identifier, count);
            }

            public long get(String identifier) {
                Integer val = elements.get(identifier);
                if (val == null) return 0;
                return val;
            }

            public int size() {
                return elements.size();
            }
        }

        class DisjointSet {
            /** Union Find / Disjoint Set data structure. */
            int[] size;
            int[] parent;
            int componentCount;

            public DisjointSet(int n) {
                componentCount = n;
                size = new int[n];
                parent = new int[n];
                for (int i=0; i<n; i++) parent[i] = i;
                for (int i=0; i<n; i++) size[i] = 1;
            }

            public void join(int a, int b) {
                /* Find roots */
                int rootA = parent[a];
                int rootB = parent[b];
                while (rootA != parent[rootA]) rootA = parent[rootA];
                while (rootB != parent[rootB]) rootB = parent[rootB];

                if (rootA == rootB) {
                    /* Already in the same set */
                    return;
                }

                /* Merge smaller set into larger set. */
                if (size[rootA] > size[rootB]) {
                    size[rootA] += size[rootB];
                    parent[rootB] = rootA;
                } else {
                    size[rootB] += size[rootA];
                    parent[rootA] = rootB;
                }
                componentCount--;
            }

        }

        class Trie {
            int N;
            int Z;
            int nextFreeId;
            int[][] pointers;
            boolean[] end;

            /** maxLenSum = maximum possible sum of length of words */
            public Trie(int maxLenSum, int alphabetSize) {
                this.N = maxLenSum;
                this.Z = alphabetSize;
                this.nextFreeId = 1;
                pointers = new int[N+1][alphabetSize];
                end = new boolean[N+1];
            }

            public void addWord(String word) {
                int curr = 0;
                for (int j=0; j<word.length(); j++) {
                    int c = word.charAt(j) - 'a';
                    int next = pointers[curr][c];
                    if (next == 0) {
                        next = nextFreeId++;
                        pointers[curr][c] = next;
                    }
                    curr = next;
                }
                end[curr] = true;
            }

            public boolean hasWord(String word) {
                int curr = 0;
                for (int j=0; j<word.length(); j++) {
                    int c = word.charAt(j) - 'a';
                    int next = pointers[curr][c];
                    if (next == 0) return false;
                    curr = next;
                }
                return end[curr];
            }

        }

        private static class Prob {

            /** For heavy calculations on probabilities, this class
             *  provides more accuracy & efficiency than doubles.
             *  Math explained: https://en.wikipedia.org/wiki/Log_probability
             *  Quick start:
             *      - Instantiate probabilities, eg. Prob a = new Prob(0.75)
             *      - add(), multiply() return new objects, can perform on nulls & NaNs.
             *      - get() returns probability as a readable double */

            /** Logarithmized probability. Note: 0% represented by logP NaN. */
            private double logP;

            /** Construct instance with real probability. */
            public Prob(double real) {
                if (real > 0) this.logP = Math.log(real);
                else this.logP = Double.NaN;
            }

            /** Construct instance with already logarithmized value. */
            static boolean dontLogAgain = true;
            public Prob(double logP, boolean anyBooleanHereToChooseThisConstructor) {
                this.logP = logP;
            }

            /** Returns real probability as a double. */
            public double get() {
                return Math.exp(logP);
            }

            @Override
            public String toString() {
                return ""+get();
            }

            /***************** STATIC METHODS BELOW ********************/

            /** Note: returns NaN only when a && b are both NaN/null. */
            public static Prob add(Prob a, Prob b) {
                if (nullOrNaN(a) && nullOrNaN(b)) return new Prob(Double.NaN, dontLogAgain);
                if (nullOrNaN(a)) return copy(b);
                if (nullOrNaN(b)) return copy(a);

                double x = Math.max(a.logP, b.logP);
                double y = Math.min(a.logP, b.logP);
                double sum = x + Math.log(1 + Math.exp(y - x));
                return new Prob(sum, dontLogAgain);
            }

            /** Note: multiplying by null or NaN produces NaN (repping 0% real prob). */
            public static Prob multiply(Prob a, Prob b) {
                if (nullOrNaN(a) || nullOrNaN(b)) return new Prob(Double.NaN, dontLogAgain);
                return new Prob(a.logP + b.logP, dontLogAgain);
            }

            /** Returns true if p is null or NaN. */
            private static boolean nullOrNaN(Prob p) {
                return (p == null || Double.isNaN(p.logP));
            }

            /** Returns a new instance with the same value as original. */
            private static Prob copy(Prob original) {
                return new Prob(original.logP, dontLogAgain);
            }
        }

        class Binary implements Comparable<Binary> {

            /**
             * Use example: Binary b = new Binary(Long.toBinaryString(53249834L));
             *
             * When manipulating small binary strings, instantiate new Binary(string)
             * When just reading large binary strings, instantiate new Binary(string,true)
             * get(int i) returns a character '1' or '0', not an int.
             */

            private boolean[] d;
            private int first; // Starting from left, the first (most remarkable) '1'
            public int length;

            public Binary(String binaryString) {
                this(binaryString, false);
            }
            public Binary(String binaryString, boolean initWithMinArraySize) {
                length = binaryString.length();
                int size = Math.max(2*length, 1);
                first = length/4;
                if (initWithMinArraySize) {
                    first = 0;
                    size = Math.max(length, 1);
                }
                d = new boolean[size];
                for (int i=0; i<length; i++) {
                    if (binaryString.charAt(i) == '1') d[i+first] = true;
                }
            }

            public void addFirst(char c) {
                if (first-1 < 0) doubleArraySize();
                first--;
                d[first] = (c == '1' ? true : false);
                length++;
            }

            public void addLast(char c) {
                if (first+length >= d.length) doubleArraySize();
                d[first+length] = (c == '1' ? true : false);
                length++;
            }

            private void doubleArraySize() {
                boolean[] bigArray = new boolean[(d.length+1) * 2];
                int newFirst = bigArray.length / 4;
                for (int i=0; i<length; i++) {
                    bigArray[i + newFirst] = d[i + first];
                }
                first = newFirst;
                d = bigArray;
            }

            public boolean flip(int i) {
                boolean value = (this.d[first+i] ? false : true);
                this.d[first+i] = value;
                return value;
            }

            public void set(int i, char c) {
                boolean value = (c == '1' ? true : false);
                this.d[first+i] = value;
            }

            public char get(int i) {
                return (this.d[first+i] ? '1' : '0');
            }

            @Override
            public int compareTo(Binary o) {
                if (this.length != o.length) return this.length - o.length;
                int len = this.length;
                for (int i=0; i<len; i++) {
                    int diff = this.get(i) - o.get(i);
                    if (diff != 0) return diff;
                }
                return 0;
            }

            @Override
            public String toString() {
                StringBuilder sb = new StringBuilder();
                for (int i=0; i<length; i++) {
                    sb.append(d[i+first] ? '1' : '0');
                }
                return sb.toString();
            }

        }

        /************************** Range queries **************************/

        class FenwickMin {
            long n;
            long[] original;
            long[] bottomUp;
            long[] topDown;

            public FenwickMin(int n) {
                this.n = n;
                original = new long[n+2];
                bottomUp = new long[n+2];
                topDown = new long[n+2];
            }

            public void set(int modifiedNode, long value) {
                long replaced = original[modifiedNode];
                original[modifiedNode] = value;
                // Update left tree
                int i = modifiedNode;
                long v = value;
                while (i <= n) {
                    if (v > bottomUp[i]) {
                        if (replaced == bottomUp[i]) {
                            v = Math.min(v, original[i]);
                            for (int r=1 ;; r++) {
                                int x = (i&-i)>>>r;
                                if (x == 0) break;
                                int child = i-x;
                                v = Math.min(v, bottomUp[child]);
                            }
                        } else break;
                    }
                    if (v == bottomUp[i]) break;
                    bottomUp[i] = v;
                    i += (i&-i);
                }
                // Update right tree
                i = modifiedNode;
                v = value;
                while (i > 0) {
                    if (v > topDown[i]) {
                        if (replaced == topDown[i]) {
                            v = Math.min(v, original[i]);
                            for (int r=1 ;; r++) {
                                int x = (i&-i)>>>r;
                                if (x == 0) break;
                                int child = i+x;
                                if (child > n+1) break;
                                v = Math.min(v, topDown[child]);
                            }
                        } else break;
                    }
                    if (v == topDown[i]) break;
                    topDown[i] = v;
                    i -= (i&-i);
                }
            }

            public long getMin(int a, int b) {
                long min = original[a];
                int prev = a;
                int curr = prev + (prev&-prev); // parent right hand side
                while (curr <= b) {
                    min = Math.min(min, topDown[prev]); // value from the other tree
                    prev = curr;
                    curr = prev + (prev&-prev);;
                }
                min = Math.min(min, original[prev]);
                prev = b;
                curr = prev - (prev&-prev); // parent left hand side
                while (curr >= a) {
                    min = Math.min(min,bottomUp[prev]); // value from the other tree
                    prev = curr;
                    curr = prev - (prev&-prev);
                }
                return min;
            }

        }

        class FenwickSum {
            public long[] d;

            public FenwickSum(int n) {
                d=new long[n+1];
            }

            /** a[0] must be unused. */
            public FenwickSum(long[] a) {
                d=new long[a.length];
                for (int i=1; i<a.length; i++) {
                    modify(i, a[i]);
                }
            }

            /** Do not modify i=0. */
            void modify(int i, long v) {
                while (i<d.length) {
                    d[i] += v;
                    // Move to next uplink on the RIGHT side of i
                    i += (i&-i);
                }
            }

            /** Returns sum from a to b, *BOTH* inclusive. */
            long getSum(int a, int b) {
                return getSum(b) - getSum(a-1);
            }

            private long getSum(int i) {
                long sum = 0;
                while (i>0) {
                    sum += d[i];
                    // Move to next uplink on the LEFT side of i
                    i -= (i&-i);
                }
                return sum;
            }
        }

        class SegmentTree {

            /* Provides log(n) operations for:
             * - Range query (sum, min or max)
             * - Range update ("+8 to all values between indexes 4 and 94")
             */

            int N;
            long[] lazy;
            long[] sum;
            long[] min;
            long[] max;
            boolean supportSum;
            boolean supportMin;
            boolean supportMax;

            public SegmentTree(int n) {
                this(n, true, true, true);
            }

            public SegmentTree(int n, boolean supportSum, boolean supportMin, boolean supportMax) {
                for (N=2; N<n;) N*=2;
                this.lazy = new long[2*N];
                this.supportSum = supportSum;
                this.supportMin = supportMin;
                this.supportMax = supportMax;
                if (this.supportSum) this.sum = new long[2*N];
                if (this.supportMin) this.min = new long[2*N];
                if (this.supportMax) this.max = new long[2*N];
            }

            void modifyRange(long x, int a, int b) {
                modifyRec(a, b, 1, 0, N-1, x);
            }

            void setRange() {
                //TODO
            }

            long getSum(int a, int b) {
                return querySum(a, b, 1, 0, N-1);
            }

            long getMin(int a, int b) {
                return queryMin(a, b, 1, 0, N-1);
            }

            long getMax(int a, int b) {
                return queryMax(a, b, 1, 0, N-1);
            }

            private long querySum(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return 0;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    int count = wantedRight - wantedLeft + 1;
                    return sum[i] + count * lazy[i];
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                long left = querySum(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1);
                long right = querySum(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight);
                return left + right;
            }

            private long queryMin(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return 0;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    return min[i] + lazy[i];
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                long left = queryMin(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1);
                long right = queryMin(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight);
                return min(left, right);
            }

            private long queryMax(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return 0;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    return max[i] + lazy[i];
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                long left = queryMax(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1);
                long right = queryMax(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight);
                return max(left, right);
            }

            private void modifyRec(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight, long value) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    lazy[i] += value;
                    return;
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                modifyRec(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1, value);
                modifyRec(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight, value);
                if (supportSum) sum[i] += value * (min(actualRight, wantedRight) - max(actualLeft, wantedLeft) + 1);
                if (supportMin) min[i] = min(min[2*i] + lazy[2*i], min[2*i+1] + lazy[2*i+1]);
                if (supportMax) max[i] = max(max[2*i] + lazy[2*i], max[2*i+1] + lazy[2*i+1]);
            }

            private void propagate(int i, int actualLeft, int actualRight) {
                lazy[2*i] += lazy[i];
                lazy[2*i+1] += lazy[i];
                if (supportSum) sum[i] += lazy[i] * (actualRight - actualLeft + 1);
                if (supportMin) min[i] += lazy[i];
                if (supportMax) max[i] += lazy[i];
                lazy[i] = 0;
            }

        }

        /***************************** Graphs *****************************/

        List<Integer>[] toGraph(IO io, int n) {
            List<Integer>[] g = new ArrayList[n+1];
            for (int i=1; i<=n; i++) g[i] = new ArrayList<>();
            for (int i=1; i<=n-1; i++) {
                int a = io.nextInt();
                int b = io.nextInt();
                g[a].add(b);
                g[b].add(a);
            }
            return g;
        }

        class Graph {
            HashMap<Long, List<Long>> edges;

            public Graph() {
                edges = new HashMap<>();
            }

            List<Long> getSetNeighbors(Long node) {
                List<Long> neighbors = edges.get(node);
                if (neighbors == null) {
                    neighbors = new ArrayList<>();
                    edges.put(node, neighbors);
                }
                return neighbors;
            }

            void addBiEdge(Long a, Long b) {
                addEdge(a, b);
                addEdge(b, a);
            }

            void addEdge(Long from, Long to) {
                getSetNeighbors(to); // make sure all have initialized lists
                List<Long> neighbors = getSetNeighbors(from);
                neighbors.add(to);
            }

            // topoSort variables
            int UNTOUCHED = 0;
            int FINISHED = 2;
            int INPROGRESS = 1;
            HashMap<Long, Integer> vis;
            List<Long> topoAns;
            List<Long> failDueToCycle = new ArrayList<Long>() {{ add(-1L); }};

            List<Long> topoSort() {
                topoAns = new ArrayList<>();
                vis = new HashMap<>();
                for (Long a : edges.keySet()) {
                    if (!topoDFS(a)) return failDueToCycle;
                }
                Collections.reverse(topoAns);
                return topoAns;
            }

            boolean topoDFS(long curr) {
                Integer status = vis.get(curr);
                if (status == null) status = UNTOUCHED;
                if (status == FINISHED) return true;
                if (status == INPROGRESS) {
                    return false;
                }
                vis.put(curr, INPROGRESS);
                for (long next : edges.get(curr)) {
                    if (!topoDFS(next)) return false;
                }
                vis.put(curr, FINISHED);
                topoAns.add(curr);
                return true;
            }

        }

        public class StronglyConnectedComponents {

            /** Kosaraju's algorithm */

            ArrayList<Integer>[] forw;
            ArrayList<Integer>[] bacw;

            /** Use: getCount(2, new int[] {1,2}, new int[] {2,1}) */
            public int getCount(int n, int[] mista, int[] minne) {
                forw = new ArrayList[n+1];
                bacw = new ArrayList[n+1];
                for (int i=1; i<=n; i++) {
                    forw[i] = new ArrayList<Integer>();
                    bacw[i] = new ArrayList<Integer>();
                }
                for (int i=0; i<mista.length; i++) {
                    int a = mista[i];
                    int b = minne[i];
                    forw[a].add(b);
                    bacw[b].add(a);
                }
                int count = 0;
                List<Integer> list = new ArrayList<Integer>();
                boolean[] visited = new boolean[n+1];
                for (int i=1; i<=n; i++) {
                    dfsForward(i, visited, list);
                }
                visited = new boolean[n+1];
                for (int i=n-1; i>=0; i--) {
                    int node = list.get(i);
                    if (visited[node]) continue;
                    count++;
                    dfsBackward(node, visited);
                }
                return count;
            }

            public void dfsForward(int i, boolean[] visited, List<Integer> list) {
                if (visited[i]) return;
                visited[i] = true;
                for (int neighbor : forw[i]) {
                    dfsForward(neighbor, visited, list);
                }
                list.add(i);
            }

            public void dfsBackward(int i, boolean[] visited) {
                if (visited[i]) return;
                visited[i] = true;
                for (int neighbor : bacw[i]) {
                    dfsBackward(neighbor, visited);
                }
            }
        }

        class LCAFinder {

            /* O(n log n) Initialize: new LCAFinder(graph)
             * O(log n) Queries: find(a,b) returns lowest common ancestor for nodes a and b */

            int[] nodes;
            int[] depths;
            int[] entries;
            int pointer;
            FenwickMin fenwick;

            public LCAFinder(List<Integer>[] graph) {
                this.nodes = new int[(int)10e6];
                this.depths = new int[(int)10e6];
                this.entries = new int[graph.length];
                this.pointer = 1;
                boolean[] visited = new boolean[graph.length+1];
                dfs(1, 0, graph, visited);
                fenwick = new FenwickMin(pointer-1);
                for (int i=1; i<pointer; i++) {
                    fenwick.set(i, depths[i] * 1000000L + i);
                }
            }

            private void dfs(int node, int depth, List<Integer>[] graph, boolean[] visited) {
                visited[node] = true;
                entries[node] = pointer;
                nodes[pointer] = node;
                depths[pointer] = depth;
                pointer++;
                for (int neighbor : graph[node]) {
                    if (visited[neighbor]) continue;
                    dfs(neighbor, depth+1, graph, visited);
                    nodes[pointer] = node;
                    depths[pointer] = depth;
                    pointer++;
                }
            }

            public int find(int a, int b) {
                int left = entries[a];
                int right = entries[b];
                if (left > right) {
                    int temp = left;
                    left = right;
                    right = temp;
                }
                long mixedBag = fenwick.getMin(left, right);
                int index = (int) (mixedBag % 1000000L);
                return nodes[index];
            }
        }

        /**************************** Geometry ****************************/

        class Point {
            int y;
            int x;

            public Point(int y, int x) {
                this.y = y;
                this.x = x;
            }
        }

        boolean segmentsIntersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
            // Returns true if segment 1-2 intersects segment 3-4

            if (x1 == x2 && x3 == x4) {
                // Both segments are vertical
                if (x1 != x3) return false;
                if (min(y1,y2) < min(y3,y4)) {
                    return max(y1,y2) >= min(y3,y4);
                } else {
                    return max(y3,y4) >= min(y1,y2);
                }
            }
            if (x1 == x2) {
                // Only segment 1-2 is vertical. Does segment 3-4 cross it? y = a*x + b
                double a34 = (y4-y3)/(x4-x3);
                double b34 = y3 - a34*x3;
                double y = a34 * x1 + b34;
                return y >= min(y1,y2) && y <= max(y1,y2) && x1 >= min(x3,x4) && x1 <= max(x3,x4);
            }
            if (x3 == x4) {
                // Only segment 3-4 is vertical. Does segment 1-2 cross it? y = a*x + b
                double a12 = (y2-y1)/(x2-x1);
                double b12 = y1 - a12*x1;
                double y = a12 * x3 + b12;
                return y >= min(y3,y4) && y <= max(y3,y4) && x3 >= min(x1,x2) && x3 <= max(x1,x2);
            }
            double a12 = (y2-y1)/(x2-x1);
            double b12 = y1 - a12*x1;
            double a34 = (y4-y3)/(x4-x3);
            double b34 = y3 - a34*x3;
            if (closeToZero(a12 - a34)) {
                // Parallel lines
                return closeToZero(b12 - b34);
            }
            // Non parallel non vertical lines intersect at x. Is x part of both segments?
            double x = -(b12-b34)/(a12-a34);
            return x >= min(x1,x2) && x <= max(x1,x2) && x >= min(x3,x4) && x <= max(x3,x4);
        }

        boolean pointInsideRectangle(Point p, List<Point> r, boolean countBorderAsInside) {
            Point a = r.get(0);
            Point b = r.get(1);
            Point c = r.get(2);
            Point d = r.get(3);
            double apd = areaOfTriangle(a, p, d);
            double dpc = areaOfTriangle(d, p, c);
            double cpb = areaOfTriangle(c, p, b);
            double pba = areaOfTriangle(p, b, a);
            double sumOfAreas = apd + dpc + cpb + pba;
            if (closeToZero(sumOfAreas - areaOfRectangle(r))) {
                if (closeToZero(apd) || closeToZero(dpc) || closeToZero(cpb) || closeToZero(pba)) {
                    return countBorderAsInside;
                }
                return true;
            }
            return false;
        }

        double areaOfTriangle(Point a, Point b, Point c) {
            return 0.5 * Math.abs((a.x-c.x)*(b.y-a.y)-(a.x-b.x)*(c.y-a.y));
        }

        double areaOfRectangle(List<Point> r) {
            double side1xDiff = r.get(0).x - r.get(1).x;
            double side1yDiff = r.get(0).y - r.get(1).y;
            double side2xDiff = r.get(1).x - r.get(2).x;
            double side2yDiff = r.get(1).y - r.get(2).y;
            double side1 = Math.sqrt(side1xDiff * side1xDiff + side1yDiff * side1yDiff);
            double side2 = Math.sqrt(side2xDiff * side2xDiff + side2yDiff * side2yDiff);
            return side1 * side2;
        }

        boolean pointsOnSameLine(double x1, double y1, double x2, double y2, double x3, double y3) {
            double areaTimes2 = x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2);
            return (closeToZero(areaTimes2));
        }

        class PointToLineSegmentDistanceCalculator {

            // Just call this
            double minDistFromPointToLineSegment(double point_x, double point_y, double x1, double y1, double x2, double y2) {
                return Math.sqrt(distToSegmentSquared(point_x, point_y, x1, y1, x2, y2));
            }

            private double distToSegmentSquared(double point_x, double point_y, double x1, double y1, double x2, double y2) {
                double l2 = dist2(x1,y1,x2,y2);
                if (l2 == 0) return dist2(point_x, point_y, x1, y1);
                double t = ((point_x - x1) * (x2 - x1) + (point_y - y1) * (y2 - y1)) / l2;
                if (t < 0) return dist2(point_x, point_y, x1, y1);
                if (t > 1) return dist2(point_x, point_y, x2, y2);

                double com_x = x1 + t * (x2 - x1);
                double com_y = y1 + t * (y2 - y1);
                return dist2(point_x, point_y, com_x, com_y);
            }

            private double dist2(double x1, double y1, double x2, double y2) {
                return Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2);
            }

        }

        /****************************** Math ******************************/

        long pow(long base, int exp) {
            if (exp == 0) return 1L;
            long x = pow(base, exp/2);
            long ans = x * x;
            if (exp % 2 != 0) ans *= base;
            return ans;
        }

        long gcd(long... v) {
            /** Chained calls to Euclidean algorithm. */
            if (v.length == 1) return v[0];
            long ans = gcd(v[1], v[0]);
            for (int i=2; i<v.length; i++) {
                ans = gcd(ans, v[i]);
            }
            return ans;
        }

        long gcd(long a, long b) {
            /** Euclidean algorithm. */
            if (b == 0) return a;
            return gcd(b, a%b);
        }

        int[] generatePrimesUpTo(int last) {
            /* Sieve of Eratosthenes. Practically O(n). Values of 0 indicate primes. */
            int[] div = new int[last+1];
            for (int x=2; x<=last; x++) {
                if (div[x] > 0) continue;
                for (int u=2*x; u<=last; u+=x) {
                    div[u] = x;
                }
            }
            return div;
        }

        long lcm(long a, long b) {
            /** Least common multiple */
            return a * b / gcd(a,b);
        }

        class BaseConverter {

            /* Palauttaa luvun esityksen kannassa base */
            public String convert(Long number, int base) {
                return Long.toString(number, base);
            }

            /* Palauttaa luvun esityksen kannassa baseTo, kun annetaan luku Stringinä kannassa baseFrom */
            public String convert(String number, int baseFrom, int baseTo) {
                return Long.toString(Long.parseLong(number, baseFrom), baseTo);
            }

            /* Tulkitsee kannassa base esitetyn luvun longiksi (kannassa 10) */
            public long longify(String number, int baseFrom) {
                return Long.parseLong(number, baseFrom);
            }
        }

        class BinomialCoefficients {
            /** Total number of K sized unique combinations from pool of size N (unordered)
             N! / ( K! (N - K)! )   */

            /** For simple queries where output fits in long. */
            public long biCo(long n, long k) {
                long r = 1;
                if (k > n) return 0;
                for (long d = 1; d <= k; d++) {
                    r *= n--;
                    r /= d;
                }
                return r;
            }

            /** For multiple queries with same n, different k. */
            public long[] precalcBinomialCoefficientsK(int n, int maxK) {
                long v[] = new long[maxK+1];
                v[0] = 1; // nC0 == 1
                for (int i=1; i<=n; i++) {
                    for (int j=Math.min(i,maxK); j>0; j--) {
                        v[j] = v[j] + v[j-1]; // Pascal's triangle
                    }
                }
                return v;
            }

            /** When output needs % MOD. */
            public long[] precalcBinomialCoefficientsK(int n, int k, long M) {
                long v[] = new long[k+1];
                v[0] = 1; // nC0 == 1
                for (int i=1; i<=n; i++) {
                    for (int j=Math.min(i,k); j>0; j--) {
                        v[j] = v[j] + v[j-1]; // Pascal's triangle
                        v[j] %= M;
                    }
                }
                return v;
            }
        }

        /**************************** Strings ****************************/

        class Zalgo {

            public int pisinEsiintyma(String haku, String kohde) {
                char[] s = new char[haku.length() + 1 + kohde.length()];
                for (int i=0; i<haku.length(); i++) {
                    s[i] = haku.charAt(i);
                }
                int j = haku.length();
                s[j++] = '#';
                for (int i=0; i<kohde.length(); i++) {
                    s[j++] = kohde.charAt(i);
                }
                int[] z = toZarray(s);
                int max = 0;
                for (int i=haku.length(); i<z.length; i++) {
                    max = Math.max(max, z[i]);
                }
                return max;
            }

            public int[] toZarray(char[] s) {
                int n = s.length;
                int[] z = new int[n];
                int a = 0, b = 0;
                for (int i = 1; i < n; i++) {
                    if (i > b) {
                        for (int j = i; j < n && s[j - i] == s[j]; j++)  z[i]++;
                    }
                    else {
                        z[i] = z[i - a];
                        if (i + z[i - a] > b) {
                            for (int j = b + 1; j < n && s[j - i] == s[j]; j++) z[i]++;
                            a = i;
                            b = i + z[i] - 1;
                        }
                    }
                }
                return z;
            }

            public List<Integer> getStartIndexesWhereWordIsFound(String haku, String kohde) {
                // this is alternative use case
                char[] s = new char[haku.length() + 1 + kohde.length()];
                for (int i=0; i<haku.length(); i++) {
                    s[i] = haku.charAt(i);
                }
                int j = haku.length();
                s[j++] = '#';
                for (int i=0; i<kohde.length(); i++) {
                    s[j++] = kohde.charAt(i);
                }
                int[] z = toZarray(s);
                List<Integer> indexes = new ArrayList<>();
                for (int i=haku.length(); i<z.length; i++) {
                    if (z[i] < haku.length()) continue;
                    indexes.add(i);
                }
                return indexes;
            }

        }

        class StringHasher {

            class HashedString {
                long[] hashes;
                long[] modifiers;

                public HashedString(long[] hashes, long[] modifiers) {
                    this.hashes = hashes;
                    this.modifiers = modifiers;
                }
            }

            long P;
            long M;

            public StringHasher() {
                initializePandM();
            }

            HashedString hashString(String s) {
                int n = s.length();
                long[] hashes = new long[n];
                long[] modifiers = new long[n];
                hashes[0] = s.charAt(0);
                modifiers[0] = 1;
                for (int i=1; i<n; i++) {
                    hashes[i] = (hashes[i-1] * P + s.charAt(i)) % M;
                    modifiers[i] = (modifiers[i-1] * P) % M;
                }
                return new HashedString(hashes, modifiers);
            }

            /**
             * Indices are inclusive.
             */
            long getHash(HashedString hashedString, int startIndex, int endIndex) {
                long[] hashes = hashedString.hashes;
                long[] modifiers = hashedString.modifiers;
                long result = hashes[endIndex];
                if (startIndex > 0) result -= (hashes[startIndex-1] * modifiers[endIndex-startIndex+1]) % M;
                if (result < 0) result += M;
                return result;
            }

            // Less interesting methods below

            /**
             * Efficient for 2 input parameter strings in particular.
             */
            HashedString[] hashString(String first, String second) {
                HashedString[] array = new HashedString[2];
                int n = first.length();
                long[] modifiers = new long[n];
                modifiers[0] = 1;

                long[] firstHashes = new long[n];
                firstHashes[0] = first.charAt(0);
                array[0] = new HashedString(firstHashes, modifiers);

                long[] secondHashes = new long[n];
                secondHashes[0] = second.charAt(0);
                array[1] = new HashedString(secondHashes, modifiers);

                for (int i=1; i<n; i++) {
                    modifiers[i] = (modifiers[i-1] * P) % M;
                    firstHashes[i] = (firstHashes[i-1] * P + first.charAt(i)) % M;
                    secondHashes[i] = (secondHashes[i-1] * P + second.charAt(i)) % M;
                }
                return array;
            }

            /**
             * Efficient for 3+ strings
             * More efficient than multiple hashString calls IF strings are same length.
             */
            HashedString[] hashString(String... strings) {
                HashedString[] array = new HashedString[strings.length];
                int n = strings[0].length();
                long[] modifiers = new long[n];
                modifiers[0] = 1;
                for (int j=0; j<strings.length; j++) {
                    // if all strings are not same length, defer work to another method
                    if (strings[j].length() != n) {
                        for (int i=0; i<n; i++) {
                            array[i] = hashString(strings[i]);
                        }
                        return array;
                    }

                    // otherwise initialize stuff
                    long[] hashes = new long[n];
                    hashes[0] = strings[j].charAt(0);
                    array[j] = new HashedString(hashes, modifiers);
                }
                for (int i=1; i<n; i++) {
                    modifiers[i] = (modifiers[i-1] * P) % M;
                    for (int j=0; j<strings.length; j++) {
                        String s = strings[j];
                        long[] hashes = array[j].hashes;
                        hashes[i] = (hashes[i-1] * P + s.charAt(i)) % M;
                    }
                }
                return array;
            }

            void initializePandM() {
                ArrayList<Long> modOptions = new ArrayList<>(20);
                modOptions.add(353873237L);
                modOptions.add(353875897L);
                modOptions.add(353878703L);
                modOptions.add(353882671L);
                modOptions.add(353885303L);
                modOptions.add(353888377L);
                modOptions.add(353893457L);
                P = modOptions.get(new Random().nextInt(modOptions.size()));

                modOptions.clear();
                modOptions.add(452940277L);
                modOptions.add(452947687L);
                modOptions.add(464478431L);
                modOptions.add(468098221L);
                modOptions.add(470374601L);
                modOptions.add(472879717L);
                modOptions.add(472881973L);
                M = modOptions.get(new Random().nextInt(modOptions.size()));

            }
        }

        int editDistance(String a, String b) {
            a = "#"+a;
            b = "#"+b;
            int n = a.length();
            int m = b.length();
            int[][] dp = new int[n+1][m+1];
            for (int y=0; y<=n; y++) {
                for (int x=0; x<=m; x++) {
                    if (y == 0) dp[y][x] = x;
                    else if (x == 0) dp[y][x] = y;
                    else {
                        int e1 = dp[y-1][x] + 1;
                        int e2 = dp[y][x-1] + 1;
                        int e3 = dp[y-1][x-1] + (a.charAt(y-1) != b.charAt(x-1) ? 1 : 0);
                        dp[y][x] = min(e1, e2, e3);
                    }
                }
            }
            return dp[n][m];
        }

        /*************************** Technical ***************************/

        private class IO extends PrintWriter {
            private InputStreamReader r;
            private static final int BUFSIZE = 1 << 15;
            private char[] buf;
            private int bufc;
            private int bufi;
            private StringBuilder sb;

            public IO() {
                super(new BufferedOutputStream(System.out));
                r = new InputStreamReader(System.in);

                buf = new char[BUFSIZE];
                bufc = 0;
                bufi = 0;
                sb = new StringBuilder();
            }

            /** Print, flush, return nextInt. */
            private int queryInt(String s) {
                io.println(s);
                io.flush();
                return nextInt();
            }

            /** Print, flush, return nextLong. */
            private long queryLong(String s) {
                io.println(s);
                io.flush();
                return nextLong();
            }

            /** Print, flush, return next word. */
            private String queryNext(String s) {
                io.println(s);
                io.flush();
                return next();
            }

            private void fillBuf() throws IOException {
                bufi = 0;
                bufc = 0;
                while(bufc == 0) {
                    bufc = r.read(buf, 0, BUFSIZE);
                    if(bufc == -1) {
                        bufc = 0;
                        return;
                    }
                }
            }

            private boolean pumpBuf() throws IOException {
                if(bufi == bufc) {
                    fillBuf();
                }
                return bufc != 0;
            }

            private boolean isDelimiter(char c) {
                return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f';
            }

            private void eatDelimiters() throws IOException {
                while(true) {
                    if(bufi == bufc) {
                        fillBuf();
                        if(bufc == 0) throw new RuntimeException("IO: Out of input.");
                    }

                    if(!isDelimiter(buf[bufi])) break;
                    ++bufi;
                }
            }

            public String next() {
                try {
                    sb.setLength(0);

                    eatDelimiters();
                    int start = bufi;

                    while(true) {
                        if(bufi == bufc) {
                            sb.append(buf, start, bufi - start);
                            fillBuf();
                            start = 0;
                            if(bufc == 0) break;
                        }

                        if(isDelimiter(buf[bufi])) break;
                        ++bufi;
                    }

                    sb.append(buf, start, bufi - start);

                    return sb.toString();
                } catch(IOException e) {
                    throw new RuntimeException("IO.next: Caught IOException.");
                }
            }

            public int nextInt() {
                try {
                    int ret = 0;

                    eatDelimiters();

                    boolean positive = true;
                    if(buf[bufi] == '-') {
                        ++bufi;
                        if(!pumpBuf()) throw new RuntimeException("IO.nextInt: Invalid int.");
                        positive = false;
                    }

                    boolean first = true;
                    while(true) {
                        if(!pumpBuf()) break;
                        if(isDelimiter(buf[bufi])) {
                            if(first) throw new RuntimeException("IO.nextInt: Invalid int.");
                            break;
                        }
                        first = false;

                        if(buf[bufi] >= '0' && buf[bufi] <= '9') {
                            if(ret < -214748364) throw new RuntimeException("IO.nextInt: Invalid int.");
                            ret *= 10;
                            ret -= (int)(buf[bufi] - '0');
                            if(ret > 0) throw new RuntimeException("IO.nextInt: Invalid int.");
                        } else {
                            throw new RuntimeException("IO.nextInt: Invalid int.");
                        }

                        ++bufi;
                    }

                    if(positive) {
                        if(ret == -2147483648) throw new RuntimeException("IO.nextInt: Invalid int.");
                        ret = -ret;
                    }

                    return ret;
                } catch(IOException e) {
                    throw new RuntimeException("IO.nextInt: Caught IOException.");
                }
            }

            public long nextLong() {
                try {
                    long ret = 0;

                    eatDelimiters();

                    boolean positive = true;
                    if(buf[bufi] == '-') {
                        ++bufi;
                        if(!pumpBuf()) throw new RuntimeException("IO.nextLong: Invalid long.");
                        positive = false;
                    }

                    boolean first = true;
                    while(true) {
                        if(!pumpBuf()) break;
                        if(isDelimiter(buf[bufi])) {
                            if(first) throw new RuntimeException("IO.nextLong: Invalid long.");
                            break;
                        }
                        first = false;

                        if(buf[bufi] >= '0' && buf[bufi] <= '9') {
                            if(ret < -922337203685477580L) throw new RuntimeException("IO.nextLong: Invalid long.");
                            ret *= 10;
                            ret -= (long)(buf[bufi] - '0');
                            if(ret > 0) throw new RuntimeException("IO.nextLong: Invalid long.");
                        } else {
                            throw new RuntimeException("IO.nextLong: Invalid long.");
                        }

                        ++bufi;
                    }

                    if(positive) {
                        if(ret == -9223372036854775808L) throw new RuntimeException("IO.nextLong: Invalid long.");
                        ret = -ret;
                    }

                    return ret;
                } catch(IOException e) {
                    throw new RuntimeException("IO.nextLong: Caught IOException.");
                }
            }

            public double nextDouble() {
                return Double.parseDouble(next());
            }

        }

        void print(Object output) {
            io.println(output);
        }

        void done(Object output) {
            print(output);
            done();
        }

        void done() {
            io.close();
            throw new RuntimeException("Clean exit");
        }

        long min(long... v) {
            long ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.min(ans, v[i]);
            }
            return ans;
        }

        double min(double... v) {
            double ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.min(ans, v[i]);
            }
            return ans;
        }

        int min(int... v) {
            int ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.min(ans, v[i]);
            }
            return ans;
        }

        long max(long... v) {
            long ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.max(ans, v[i]);
            }
            return ans;
        }

        double max(double... v) {
            double ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.max(ans, v[i]);
            }
            return ans;
        }

        int max(int... v) {
            int ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.max(ans, v[i]);
            }
            return ans;
        }

    }

}

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;

/**
 * Created by timur on 28.03.15.
 */

public class TaskD {
    boolean eof;
    BufferedReader br;
    StringTokenizer st;
    PrintWriter out;

    public static void main(String[] args) throws IOException {
        new TaskD().run();
    }

    public String nextToken() {
        while (st == null || !st.hasMoreTokens()) {
            try {
                st = new StringTokenizer(br.readLine());
            } catch (Exception e) {
                eof = true;
                return "-1";
            }
        }
        return st.nextToken();
    }

    public int nextInt() {
        return Integer.parseInt(nextToken());
    }

    public long nextLong() {
        return Long.parseLong(nextToken());
    }

    double nextDouble() {
        return Double.parseDouble(nextToken());
    }

    String nextLine() throws IOException {
        return br.readLine();
    }

    void run() throws IOException {
        InputStream input = System.in;
        PrintStream output = System.out;
        try {
            File f = new File("a.in");
            if (f.exists() && f.canRead()) {
                input = new FileInputStream(f);
                output = new PrintStream("a.out");
            }
        } catch (Throwable e) {
        }
        br = new BufferedReader(new InputStreamReader(input));
        out = new PrintWriter(output);
        solve();
        br.close();
        out.close();
    }

    long gcd(long a, long b) {
        if (b == 0)
            return a;
        else
            return gcd(b, a % b);
    }

    class Pair implements Comparable<Pair> {
        int move, change, num;

        public Pair(int move, int change, int num) {
            this.move = move;
            this.change = change;
            this.num = num;

        }

        @Override
        public int compareTo(Pair o) {
            int r = move - o.move;
            if (r == 0)
                r = this.change - o.change;
            return r;
        }

        public Pair up() {
            return new Pair(move + 1, change + 1, num);
        }
    }

    Pair min(Pair a, Pair b) {
        if (a == null)
            return b;
        if (b == null)
            return a;
        if (b.compareTo(a) < 0)
            return b;
        return a;
    }

    Pair numin(Pair a, Pair b) {
        if (a == null)
            return b;
        if (b == null)
            return a;
        if (a.num > b.num)
            return a;
        if (a.num < b.num)
            return b;
        return min(a, b);
    }

    class map {
        Pair[][] m;

        public map() {
            m = new Pair[n][n];
        }
    }

    int n, k;

    void solve() {
        n = nextInt();
        k = n * n;
        int[] x = new int[k + 1];
        int[] y = new int[k + 1];
        int u;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                u = nextInt() - 1;
                x[u] = i;
                y[u] = j;
            }
        }
        int inf = k * 2 + 5;
        map[] h = new map[inf];
        map[] s = new map[inf];
        map[] l = new map[inf];
        for (int i = 0; i < inf; i++) {
            h[i] = new map();
            s[i] = new map();
            l[i] = new map();
        }
        h[0].m[x[0]][y[0]] = new Pair(0, 0, 1);
        l[0].m[x[0]][y[0]] = new Pair(0, 0, 1);
        s[0].m[x[0]][y[0]] = new Pair(0, 0, 1);

        int[] dxh = {-2, -2, -1, 1, 2, 2, 1, -1};
        int[] dyh = {-1, 1, 2, 2, 1, -1, -2, -2};

        int[] dxs = {-1, -1, 1, 1};
        int[] dys = {-1, 1, 1, -1};

        int[] dxl = {-1, 0, 1, 0};
        int[] dyl = {0, 1, 0, -1};

        int nx, ny;
        Pair np;
        Pair cp;

        Pair ans = null;
        for (int i = 0; i < inf - 3; i++) {
            for (int xx = 0; xx < n; xx++) {
                for (int yy = 0; yy < n; yy++) {
                    cp = h[i].m[xx][yy];
                    if (cp != null) {
                        for (int j = 0; j < 8; j++) {
                            nx = xx + dxh[j];
                            ny = yy + dyh[j];
                            if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                                np = new Pair(cp.move + 1, cp.change, cp.num);
                                if (cp.num < k && nx == x[cp.num] && ny == y[cp.num]) {
                                    np = new Pair(cp.move + 1, cp.change, cp.num + 1);
                                }
                                h[i + 1].m[nx][ny] = numin(h[i + 1].m[nx][ny], np);
                                s[i + 2].m[nx][ny] = numin(s[i + 2].m[nx][ny], np.up());
                                l[i + 2].m[nx][ny] = numin(l[i + 2].m[nx][ny], np.up());
                                ans = numin(ans, np);
                            }
                        }
                    }
                    cp = s[i].m[xx][yy];
                    if (cp != null) {
                        for (int j = 0; j < 4; j++) {
                            for (int mul = 1; mul < n + 1; mul++) {
                                nx = xx + dxs[j] * mul;
                                ny = yy + dys[j] * mul;
                                if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                                    np = new Pair(cp.move + 1, cp.change, cp.num);
                                    if (cp.num < k && nx == x[cp.num] && ny == y[cp.num]) {
                                        np = new Pair(cp.move + 1, cp.change, cp.num + 1);
                                    }
                                    s[i + 1].m[nx][ny] = numin(s[i + 1].m[nx][ny], np);
                                    h[i + 2].m[nx][ny] = numin(h[i + 2].m[nx][ny], np.up());
                                    l[i + 2].m[nx][ny] = numin(l[i + 2].m[nx][ny], np.up());
                                    ans = numin(ans, np);
                                } else {
                                    break;
                                }
                            }
                        }
                    }
                    cp = l[i].m[xx][yy];
                    if (cp != null) {
                        for (int j = 0; j < 4; j++) {
                            for (int mul = 1; mul < n + 1; mul++) {
                                nx = xx + dxl[j] * mul;
                                ny = yy + dyl[j] * mul;
                                if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                                    np = new Pair(cp.move + 1, cp.change, cp.num);
                                    if (cp.num < k && nx == x[cp.num] && ny == y[cp.num]) {
                                        np = new Pair(cp.move + 1, cp.change, cp.num + 1);
                                    }
                                    l[i + 1].m[nx][ny] = numin(l[i + 1].m[nx][ny], np);
                                    s[i + 2].m[nx][ny] = numin(s[i + 2].m[nx][ny], np.up());
                                    h[i + 2].m[nx][ny] = numin(h[i + 2].m[nx][ny], np.up());
                                    ans = numin(ans, np);
                                } else {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        out.print(ans.move + " " + ans.change);
        /*

        for (int i = 1; i < k; i++) {
            if (x[i] == x[i - 1] || y[i] == y[i - 1]) {
                l[i] = l[i - 1] + 1;
            } else {
                l[i] = l[i - 1] + 2;
            }
            zl[i] = zl[i - 1];
            s[i] = l[i] + 1;
            zs[i] = zl[i - 1] + 1;
            h[i] = l[i] + 1;
            zh[i] = zl[i - 1] + 1;
            if ((x[i] + y[i]) % 2 == (x[i - 1] + y[i - 1]) % 2) {
                if ((x[i] - y[i] == x[i - 1] - y[i - 1]) ||
                    (x[i] + y[i] == x[i - 1] + y[i - 1])) {
                    s[i] = s[i - 1] + 1;
                } else {
                    s[i] = s[i - 1] + 2;
                    if (s[i] + 1 < l[i] || (s[i] + 1 == l[i] && zs[i] + 1 < zl[i])) {
                        l[i] = s[i] + 1;
                        zl[i] = zs[i] + 1;
                    }
                    if (s[i] + 1 < h[i] || (s[i] + 1 == h[i] && zs[i] + 1 < zh[i])) {
                        h[i] = s[i] + 1;
                        zh[i] = zs[i] + 1;
                    }
                }
            }
            int mv = inf;
            Queue<Integer> xx = new ArrayDeque<>();
            Queue<Integer> yy = new ArrayDeque<>();
            Queue<Integer> c = new ArrayDeque<>();
            xx.add(x[i - 1]);
            yy.add(y[i - 1]);
            c.add(0);
            boolean[][] vis = new boolean[n][n];
            vis[x[i - 1]][y[i - 1]] = true;
            while (!xx.isEmpty()) {
                cx = xx.poll();
                cy = yy.poll();
                cc = c.poll();
                for (int j = 0; j < 8; j++) {
                    nx = cx + dx[j];
                    ny = cy + dy[j];
                    if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                        if (nx == x[i] && ny == y[i]) {
                            mv = cc + 1;
                            xx.clear();
                            break;
                        }
                        if (!vis[nx][ny]) {
                            xx.add(nx);
                            yy.add(ny);
                            c.add(cc + 1);
                            vis[nx][ny] = true;
                        }
                    }
                }
            }
            if (h[i - 1] + mv < h[i] || (h[i - 1] + mv == h[i] && zh[i] > zh[i - 1])) {
                h[i] = h[i - 1] + mv;
                zh[i] = zh[ i - 1];
            }
            if (h[i] + 1 < l[i] || (h[i] + 1 == l[i] && zh[i] + 1 < zl[i])) {
                l[i] = h[i] + 1;
                zl[i] = zh[i] + 1;
            }
            if (h[i] + 1 < s[i] || (h[i] + 1 == s[i] && zh[i] + 1 < zs[i])) {
                s[i] = h[i] + 1;
                zs[i] = zh[i] + 1;
            }
        }
        Pair lll = new Pair(l[k - 1], zl[k - 1]);
        Pair hhh = new Pair(h[k - 1], zh[k - 1]);
        Pair sss = new Pair(s[k - 1], zs[k - 1]);
        Pair res = lll;
        if (res.compareTo(hhh) > 0) {
            res = hhh;
        }
        if (res.compareTo(sss) > 0) {
            res = sss;
        }
        out.print(res.l + " " + res.change);
        */
    }

}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;

/* spar5h */

public class cf2{

	static int[][] dp, dp2, d, d2;
	static int[] ai, aj;

	static void update(int s_i, int f_i, int s_j, int f_j, int val, int c_i, int c_j, int rep) {

		for(int i = s_i; i <= f_i; i++) {

			for(int j = s_j; j <= f_j; j++) {

				int rpl = rep;

				if(i != c_i)
					rpl++;

				if(j != c_j)
					rpl++;

				if(d[i][j] > val + rpl) {
					d[i][j] = val + rpl; d2[i][j] = rpl;
				}
				else if(d[i][j] == val + rpl)
					d2[i][j] = min(rpl, d2[i][j]);

			}
		}
	}

	static boolean rook(int si, int sj, int ti, int tj) {

		if(si == ti || sj == tj)
			return true;

		return false;
	}

	static boolean bishop(int si, int sj, int ti, int tj) {

		if(abs(si - ti) == abs(sj - tj))
			return true;

		return false;
	}

	static boolean knight(int si, int sj, int ti, int tj) {

		if((abs(si - ti) == 1 && abs(sj - tj) == 2) || (abs(si - ti) == 2 && abs(sj - tj) == 1))
			return true;

		return false;
	}

	static void recur(int x, int n) {

		//rook: 0, bishop; 1, knight: 2

		if(x + 1 < n * n - 1)
			recur(x + 1, n);

		int si = ai[x], sj = aj[x], ti = ai[x + 1], tj = aj[x + 1];

		d = new int[3][3]; d2 = new int[3][3];

		for(int i = 0; i < 3; i++)
			for(int j = 0; j < 3; j++)
				d[i][j] = Integer.MAX_VALUE;

		update(0, 2, 0, 2, 2, 0, 0, 0);

		//1
		if(rook(si, sj, ti, tj))
			update(0, 2, 0, 2, 1, 0, 0, 0);

		if(bishop(si, sj, ti, tj))
			update(0, 2, 0, 2, 1, 1, 1, 0);

		if(knight(si, sj, ti, tj))
			update(0, 2, 0, 2, 1, 2, 2, 0);

		//2
		for(int xi = 0; xi < n; xi++) {

			for(int xj = 0; xj < n; xj++) {

				if(bishop(si, sj, xi, xj) && bishop(xi, xj, ti, tj))
					update(1, 2, 1, 2, 2, 1, 1, 0);

				if(knight(si, sj, xi, xj) && knight(xi, xj, ti, tj))
					update(1, 2, 1, 2, 2, 2, 2, 0);

				if(bishop(si, sj, xi, xj) && knight(xi, xj, ti, tj))
					update(1, 1, 2, 2, 2, 1, 2, 1);

				if(knight(si, sj, xi, xj) && bishop(xi, xj, ti, tj))
					update(2, 2, 1, 1, 2, 2, 1, 1);
			}
		}

		//3
		for(int xi = 0; xi < n; xi++) {

			for(int xj = 0; xj < n; xj++) {

				for(int yi = 0; yi < n; yi++) {

					for(int yj = 0; yj < n; yj++) {

						if(bishop(si, sj, xi, xj) && bishop(xi, xj, yi, yj) && bishop(yi, yj, ti, tj))
							update(1, 1, 1, 1, 3, 1, 1, 0);

						if(knight(si, sj, xi, xj) && knight(xi, xj, yi, yj) && knight(yi, yj, ti, tj)) {
							update(2, 2, 2, 2, 3, 2, 2, 0);
						}
					}
				}
			}
		}

		for(int i = 0; i < 3; i++)
			dp[x][i] = Integer.MAX_VALUE;

		for(int i = 0; i < 3; i++) {
			for(int j = 0; j < 3; j++) {

				if(dp[x][i] > d[i][j] + dp[x + 1][j]) {
					dp[x][i] = d[i][j] + dp[x + 1][j]; dp2[x][i] = d2[i][j] + dp2[x + 1][j];
				}
				else if(dp[x][i] == d[i][j] + dp[x + 1][j])
					dp2[x][i] = min(d2[i][j] + dp2[x + 1][j], dp2[x][i]);
			}
		}
	}

	public static void main(String[] args) {

		InputReader s = new InputReader(System.in);
		PrintWriter w = new PrintWriter(System.out);

		int n = s.nextInt();

		ai = new int[n * n];
		aj = new int[n * n];

		for(int i = 0; i < n; i++) {

			for(int j = 0; j < n; j++) {

				int x = s.nextInt() - 1;
				ai[x] = i; aj[x] = j;
			}
		}

		dp = new int[n * n][3];
		dp2 = new int[n * n][3];

		recur(0, n);

		int v1 = Integer.MAX_VALUE, v2 = 0;

		for(int i = 0; i < 3; i++) {

			if(dp[0][i] < v1) {
				v1 = dp[0][i]; v2 = dp2[0][i];
			}
			else if(dp[0][i] == v1)
				v2 = min(dp2[0][i], v2);
		}

		w.println(v1 + " " + v2);

		w.close();
	}

	static class InputReader {

		private InputStream stream;
		private byte[] buf = new byte[1024];
		private int curChar;
		private int numChars;
		private SpaceCharFilter filter;

		public InputReader(InputStream stream)
		{
			this.stream = stream;
		}

		public int read()
		{
			if (numChars==-1)
				throw new InputMismatchException();

			if (curChar >= numChars)
			{
				curChar = 0;
				try
				{
					numChars = stream.read(buf);
				}
				catch (IOException e)
				{
					throw new InputMismatchException();
				}

				if(numChars <= 0)
					return -1;
			}
			return buf[curChar++];
		}

		public String nextLine()
		{
			BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
			String str = "";
            try
            {
                str = br.readLine();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            return str;
		}
		public int nextInt()
		{
			int c = read();

			while(isSpaceChar(c))
				c = read();

			int sgn = 1;

			if (c == '-')
			{
				sgn = -1;
				c = read();
			}

			int res = 0;
			do
			{
				if(c<'0'||c>'9')
					throw new InputMismatchException();
				res *= 10;
				res += c - '0';
				c = read();
			}
			while (!isSpaceChar(c));

			return res * sgn;
		}

		public long nextLong()
		{
			int c = read();
			while (isSpaceChar(c))
				c = read();
			int sgn = 1;
			if (c == '-')
			{
				sgn = -1;
				c = read();
			}
			long res = 0;

			do
			{
				if (c < '0' || c > '9')
					throw new InputMismatchException();
				res *= 10;
				res += c - '0';
				c = read();
			}
			while (!isSpaceChar(c));
				return res * sgn;
		}

		public double nextDouble()
		{
			int c = read();
			while (isSpaceChar(c))
				c = read();
			int sgn = 1;
			if (c == '-')
			{
				sgn = -1;
				c = read();
			}
			double res = 0;
			while (!isSpaceChar(c) && c != '.')
			{
				if (c == 'e' || c == 'E')
					return res * Math.pow(10, nextInt());
				if (c < '0' || c > '9')
					throw new InputMismatchException();
				res *= 10;
				res += c - '0';
				c = read();
			}
			if (c == '.')
			{
				c = read();
				double m = 1;
				while (!isSpaceChar(c))
				{
					if (c == 'e' || c == 'E')
						return res * Math.pow(10, nextInt());
					if (c < '0' || c > '9')
						throw new InputMismatchException();
					m /= 10;
					res += (c - '0') * m;
					c = read();
				}
			}
			return res * sgn;
		}

		public String readString()
		{
			int c = read();
			while (isSpaceChar(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do
			{
				res.appendCodePoint(c);
				c = read();
			}
			while (!isSpaceChar(c));

			return res.toString();
		}

		public boolean isSpaceChar(int c)
		{
			if (filter != null)
				return filter.isSpaceChar(c);
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		public String next()
		{
			return readString();
		}

		public interface SpaceCharFilter
		{
			public boolean isSpaceChar(int ch);
		}
	}

}

import java.io.*;
import java.math.*;
import java.util.*;

public class ThreePieces {
    private static final int INF = 1234567890;

    public static void solve(FastIO io) {
        int N = io.nextInt();

        int r0 = -1, c0 = -1;
        int[][] A = new int[N][];
        for (int i = 0; i < N; ++i) {
            A[i] = io.nextIntArray(N);

            for (int j = 0; j < N; ++j) {
                if (A[i][j] == 1) {
                    r0 = i;
                    c0 = j;
                }
            }
        }

        LocList mb = new LocList();
        for (int i = 1; i <= N; ++i) {
            mb.add(new Loc(i, i));
            mb.add(new Loc(i, -i));
            mb.add(new Loc(-i, i));
            mb.add(new Loc(-i, -i));
        }

        LocList mk = new LocList();
        mk.add(new Loc(-2, 1));
        mk.add(new Loc(-1, 2));
        mk.add(new Loc(1, 2));
        mk.add(new Loc(2, 1));
        mk.add(new Loc(2, -1));
        mk.add(new Loc(1, -2));
        mk.add(new Loc(-1, -2));
        mk.add(new Loc(-2, -1));

        LocList mr = new LocList();
        for (int i = 1; i <= N; ++i) {
            mr.add(new Loc(0, i));
            mr.add(new Loc(0, -i));
            mr.add(new Loc(i, 0));
            mr.add(new Loc(-i, 0));
        }

        LocList[] moves = {mb, mk, mr};

        int N2 = N * N;
        int[][][][] dp = megaInit(N);
        dp[r0][c0][1][0] = dp[r0][c0][1][1] = dp[r0][c0][1][2] = 0;

        for (int t = 0;; ++t) {
            int[][][][] next = megaInit(N);
            int bestFinish = INF;
            for (int r = 0; r < N; ++r) {
                for (int c = 0; c < N; ++c) {
                    for (int i = 1; i < N2; ++i) {
                        for (int p = 0; p < 3; ++p) {
                            for (int np = 0; np < 3; ++np) {
                                next[r][c][i][np] = Math.min(next[r][c][i][np], 1 + dp[r][c][i][p]);
                            }
                            if (dp[r][c][i][p] < INF) {
                                for (Loc mv : moves[p]) {
                                    int nr = r + mv.R;
                                    int nc = c + mv.C;
                                    if (nr >= 0 && nr < N && nc >= 0 && nc < N) {
                                        int ni = i;
                                        if (A[nr][nc] == i + 1) {
                                            ++ni;
                                        }
                                        next[nr][nc][ni][p] = Math.min(next[nr][nc][ni][p], dp[r][c][i][p]);
                                    }
                                }
                            }
                        }
                    }
                    for (int p = 0; p < 3; ++p) {
                        bestFinish = Math.min(bestFinish, dp[r][c][N2][p]);
                    }
                }
            }
            if (bestFinish < INF) {
                io.printf("%d %d\n", t, bestFinish);
                return;
            }
            dp = next;
        }
    }

    private static int[][][][] megaInit(int N) {
        final int N2 = N * N;
        int[][][][] arr = new int[N][N][N2 + 1][3];
        for (int r = 0; r < N; ++r) {
            for (int c = 0; c < N; ++c) {
                for (int i = 0; i <= N2; ++i) {
                    Arrays.fill(arr[r][c][i], INF);
                }
            }
        }
        return arr;
    }

    private static class LocList extends ArrayList<Loc> {
    }

    private static class Loc {
        public int R, C;

        public Loc(int r, int c) {
            this.R = r;
            this.C = c;
        }
    }

    public static class FastIO {
        private InputStream reader;
        private PrintWriter writer;

        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;

        public FastIO(InputStream r, OutputStream w) {
            reader = r;
            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(w)));
        }

        public int read() {
            if (numChars == -1)
                throw new InputMismatchException();
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = reader.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (numChars <= 0)
                    return -1;
            }
            return buf[curChar++];
        }

        public String nextLine() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isEndOfLine(c));
            return res.toString();
        }

        public String nextString() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isSpaceChar(c));
            return res.toString();
        }

        public long nextLong() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            long res = 0;
            do {
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public int nextInt() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public int[] nextIntArray(int n) {
            return nextIntArray(n, 0);
        }

        public int[] nextIntArray(int n, int off) {
            int[] arr = new int[n + off];
            for (int i = 0; i < n; i++) {
                arr[i + off] = nextInt();
            }
            return arr;
        }

        public long[] nextLongArray(int n) {
            return nextLongArray(n, 0);
        }

        public long[] nextLongArray(int n, int off) {
            long[] arr = new long[n + off];
            for (int i = 0; i < n; i++) {
                arr[i + off] = nextLong();
            }
            return arr;
        }

        private boolean isSpaceChar(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        private boolean isEndOfLine(int c) {
            return c == '\n' || c == '\r' || c == -1;
        }

        public void print(Object... objects) {
            for (int i = 0; i < objects.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(objects[i]);
            }
        }

        public void println(Object... objects) {
            print(objects);
            writer.println();
        }

        public void printArray(int[] arr) {
            for (int i = 0; i < arr.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(arr[i]);
            }
        }

        public void printArray(long[] arr) {
            for (int i = 0; i < arr.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(arr[i]);
            }
        }

        public void printlnArray(int[] arr) {
            printArray(arr);
            writer.println();
        }

        public void printlnArray(long[] arr) {
            printArray(arr);
            writer.println();
        }

        public void printf(String format, Object... args) {
            print(String.format(format, args));
        }

        public void flush() {
            writer.flush();
        }
    }

    public static void main(String[] args) {
        FastIO io = new FastIO(System.in, System.out);
        solve(io);
        io.flush();
    }
}

import java.io.*;
import java.math.*;
import java.util.*;

public class ThreePieces {
    private static final int INF = 1234567890;

    public static void solve(FastIO io) {
        int N = io.nextInt();

        int r0 = -1, c0 = -1;
        int[][] A = new int[N][];
        for (int i = 0; i < N; ++i) {
            A[i] = io.nextIntArray(N);

            for (int j = 0; j < N; ++j) {
                if (A[i][j] == 1) {
                    r0 = i;
                    c0 = j;
                }
            }
        }

        LocList mb = new LocList();
        for (int i = 1; i <= N; ++i) {
            mb.add(new Loc(i, i));
            mb.add(new Loc(i, -i));
            mb.add(new Loc(-i, i));
            mb.add(new Loc(-i, -i));
        }

        LocList mk = new LocList();
        mk.add(new Loc(-2, 1));
        mk.add(new Loc(-1, 2));
        mk.add(new Loc(1, 2));
        mk.add(new Loc(2, 1));
        mk.add(new Loc(2, -1));
        mk.add(new Loc(1, -2));
        mk.add(new Loc(-1, -2));
        mk.add(new Loc(-2, -1));

        LocList mr = new LocList();
        for (int i = 1; i <= N; ++i) {
            mr.add(new Loc(0, i));
            mr.add(new Loc(0, -i));
            mr.add(new Loc(i, 0));
            mr.add(new Loc(-i, 0));
        }

        LocList[] moves = {mb, mk, mr};

        int N2 = N * N;
        int[][][][] dp = megaInit(N);
        dp[r0][c0][1][0] = dp[r0][c0][1][1] = dp[r0][c0][1][2] = 0;

        for (int t = 0;; ++t) {
            int[][][][] next = megaInit(N);
            int bestFinish = INF;
            for (int r = 0; r < N; ++r) {
                for (int c = 0; c < N; ++c) {
                    for (int i = 1; i < N2; ++i) {
                        for (int p = 0; p < 3; ++p) {
                            for (int np = 0; np < 3; ++np) {
                                next[r][c][i][np] = Math.min(next[r][c][i][np], 1 + dp[r][c][i][p]);
                            }
                            if (dp[r][c][i][p] < INF) {
                                for (Loc mv : moves[p]) {
                                    int nr = r + mv.R;
                                    int nc = c + mv.C;
                                    if (nr >= 0 && nr < N && nc >= 0 && nc < N) {
                                        int ni = i;
                                        if (A[nr][nc] == i + 1) {
                                            ++ni;
                                        }
                                        next[nr][nc][ni][p] = Math.min(next[nr][nc][ni][p], dp[r][c][i][p]);
                                    }
                                }
                            }
                        }
                    }
                    for (int p = 0; p < 3; ++p) {
                        bestFinish = Math.min(bestFinish, dp[r][c][N2][p]);
                        // if (dp[r][c][N2][p] < INF) {
                        //     System.out.format("r = %d, c = %d, p = %d, got: %d\n", r, c, p, dp[r][c][N2][p]);
                        // }
                    }
                }
            }
            if (bestFinish < INF) {
                io.printf("%d %d\n", t, bestFinish);
                return;
            }
            dp = next;
        }
    }

    private static int[][][][] megaInit(int N) {
        final int N2 = N * N;
        int[][][][] arr = new int[N][N][N2 + 1][3];
        for (int r = 0; r < N; ++r) {
            for (int c = 0; c < N; ++c) {
                for (int i = 0; i <= N2; ++i) {
                    Arrays.fill(arr[r][c][i], INF);
                }
            }
        }
        return arr;
    }

    private static class LocList extends ArrayList<Loc> {
    }

    private static class Loc {
        public int R, C;

        public Loc(int r, int c) {
            this.R = r;
            this.C = c;
        }
    }

    public static class FastIO {
        private InputStream reader;
        private PrintWriter writer;

        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;

        public FastIO(InputStream r, OutputStream w) {
            reader = r;
            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(w)));
        }

        public int read() {
            if (numChars == -1)
                throw new InputMismatchException();
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = reader.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (numChars <= 0)
                    return -1;
            }
            return buf[curChar++];
        }

        public String nextLine() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isEndOfLine(c));
            return res.toString();
        }

        public String nextString() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isSpaceChar(c));
            return res.toString();
        }

        public long nextLong() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            long res = 0;
            do {
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public int nextInt() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public int[] nextIntArray(int n) {
            return nextIntArray(n, 0);
        }

        public int[] nextIntArray(int n, int off) {
            int[] arr = new int[n + off];
            for (int i = 0; i < n; i++) {
                arr[i + off] = nextInt();
            }
            return arr;
        }

        public long[] nextLongArray(int n) {
            return nextLongArray(n, 0);
        }

        public long[] nextLongArray(int n, int off) {
            long[] arr = new long[n + off];
            for (int i = 0; i < n; i++) {
                arr[i + off] = nextLong();
            }
            return arr;
        }

        private boolean isSpaceChar(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        private boolean isEndOfLine(int c) {
            return c == '\n' || c == '\r' || c == -1;
        }

        public void print(Object... objects) {
            for (int i = 0; i < objects.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(objects[i]);
            }
        }

        public void println(Object... objects) {
            print(objects);
            writer.println();
        }

        public void printArray(int[] arr) {
            for (int i = 0; i < arr.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(arr[i]);
            }
        }

        public void printArray(long[] arr) {
            for (int i = 0; i < arr.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(arr[i]);
            }
        }

        public void printlnArray(int[] arr) {
            printArray(arr);
            writer.println();
        }

        public void printlnArray(long[] arr) {
            printArray(arr);
            writer.println();
        }

        public void printf(String format, Object... args) {
            print(String.format(format, args));
        }

        public void flush() {
            writer.flush();
        }
    }

    public static void main(String[] args) {
        FastIO io = new FastIO(System.in, System.out);
        solve(io);
        io.flush();
    }
}

/*
If you want to aim high, aim high
Don't let that studying and grades consume you
Just live life young
******************************
What do you think? What do you think?
1st on Billboard, what do you think of it
Next is a Grammy, what do you think of it
However you think, I’m sorry, but shit, I have no fcking interest
*******************************
I'm standing on top of my Monopoly board
That means I'm on top of my game and it don't stop
til my hip don't hop anymore
https://www.a2oj.com/Ladder16.html
*******************************
Shining through the city with a little funk and soul
*******************************
300iq as writer = Sad!
*/
import java.util.*;
import java.io.*;
import java.math.*;

   public class x1065D
   {
      public static void main(String hi[]) throws Exception
      {
         BufferedReader infile = new BufferedReader(new InputStreamReader(System.in));
         StringTokenizer st = new StringTokenizer(infile.readLine());
         int N = Integer.parseInt(st.nextToken());
         int[][] grid = new int[N][N];
         for(int i=0; i < N; i++)
         {
            st = new StringTokenizer(infile.readLine());
            for(int a=0; a < N; a++)
               grid[i][a] = Integer.parseInt(st.nextToken());
         }
         int[] loc = new int[N*N+1];
         for(int r=0; r < N; r++)
            for(int c=0; c < N; c++)
               loc[grid[r][c]] = r*N+c;
         //knight bishop rook
         int[][][] dist = new int[N*N+1][3][N*N+1];
         int[][][] replace = new int[N*N+1][3][N*N+1];
         for(int i=0; i <= N*N; i++)
            for(int p=0; p < 3; p++)
            {
               Arrays.fill(dist[i][p], 420420420);
               Arrays.fill(replace[i][p], 420420420);
            }
         int start = loc[1];
         for(int p=0; p < 3; p++)
            dist[start][p][1] = replace[start][p][1] = 0;
         ArrayDeque<Integer> q = new ArrayDeque<Integer>();
         for(int p=0; p < 3; p++)
         {
            q.add(start);
            q.add(p);
            q.add(1);
         }
         while(q.size() > 0)
         {
            int tempo = q.poll();
            int piece = q.poll();
            int target = q.poll();
            int x = tempo/N;
            int y = tempo%N;
            //swap
            for(int p=0; p < 3; p++)
            {
               if(piece == p)
                  continue;
               if(replace[tempo][p][target] > replace[tempo][piece][target]+1 && dist[tempo][p][target] == dist[tempo][piece][target]+1)
               {
                  replace[tempo][p][target] = replace[tempo][piece][target]+1;
                  q.add(tempo);
                  q.add(p);
                  q.add(target);
               }
               else if(dist[tempo][p][target] > dist[tempo][piece][target]+1)
               {
                  replace[tempo][p][target] = replace[tempo][piece][target]+1;
                  dist[tempo][p][target] = dist[tempo][piece][target]+1;
                  q.add(tempo);
                  q.add(p);
                  q.add(target);
               }
            }
            //move
            if(piece == 0)
            {
               for(int dx=-2; dx <= 2; dx++)
                  for(int dy=-2; dy <= 2; dy++)
                  {
                     if(dx == 0 || dy == 0 || Math.abs(dx) == Math.abs(dy))
                        continue;
                     int r = x+dx;
                     int c = y+dy;
                     if(Math.min(r,c) < 0 || Math.max(r,c) >= N)
                        continue;
                     int id = target+(grid[r][c]-1 == target ? 1 : 0);
                     if(dist[r*N+c][piece][id] > dist[tempo][piece][target]+1)
                     {
                        replace[r*N+c][piece][id] = replace[tempo][piece][target];
                        dist[r*N+c][piece][id] = dist[tempo][piece][target]+1;
                        q.add(r*N+c);
                        q.add(piece);
                        q.add(id);
                     }
                     else if(dist[r*N+c][piece][id] == dist[tempo][piece][target]+1 && replace[r*N+c][piece][id] > replace[tempo][piece][target])
                     {
                        replace[r*N+c][piece][id] = replace[tempo][piece][target];
                        q.add(r*N+c);
                        q.add(piece);
                        q.add(id);
                     }
                  }
            }
            else if(piece == 1)
            {
               for(int a=1; a < N; a++)
                  for(int dx=-1; dx <= 1; dx+=2)
                     for(int dy=-1; dy <= 1; dy+=2)
                     {
                        int r = x+dx*a;
                        int c = y+dy*a;
                        if(Math.min(r,c) < 0 || Math.max(r,c) >= N)
                           continue;
                        int id = target+(grid[r][c]-1 == target ? 1 : 0);
                        if(dist[r*N+c][piece][id] > dist[tempo][piece][target]+1)
                        {
                           replace[r*N+c][piece][id] = replace[tempo][piece][target];
                           dist[r*N+c][piece][id] = dist[tempo][piece][target]+1;
                           q.add(r*N+c);
                           q.add(piece);
                           q.add(id);
                        }
                        else if(dist[r*N+c][piece][id] == dist[tempo][piece][target]+1 && replace[r*N+c][piece][id] > replace[tempo][piece][target])
                        {
                           replace[r*N+c][piece][id] = replace[tempo][piece][target];
                           q.add(r*N+c);
                           q.add(piece);
                           q.add(id);
                        }
                     }
            }
            else
            {
               for(int a=1; a < N; a++)
                  for(int dx=-1; dx <= 1; dx+=2)
                  {
                     int r = x+dx*a;
                     int c = y;
                     if(Math.min(r,c) < 0 || Math.max(r,c) >= N)
                        continue;
                     int id = target+(grid[r][c]-1 == target ? 1 : 0);
                     if(dist[r*N+c][piece][id] > dist[tempo][piece][target]+1)
                     {
                        replace[r*N+c][piece][id] = replace[tempo][piece][target];
                        dist[r*N+c][piece][id] = dist[tempo][piece][target]+1;
                        q.add(r*N+c);
                        q.add(piece);
                        q.add(id);
                     }
                     else if(dist[r*N+c][piece][id] == dist[tempo][piece][target]+1 && replace[r*N+c][piece][id] > replace[tempo][piece][target])
                     {
                        replace[r*N+c][piece][id] = replace[tempo][piece][target];
                        q.add(r*N+c);
                        q.add(piece);
                        q.add(id);
                     }
                  }
               for(int a=1; a < N; a++)
                  for(int dy=-1; dy <= 1; dy+=2)
                  {
                     int r = x;
                     int c = y+dy*a;
                     if(Math.min(r,c) < 0 || Math.max(r,c) >= N)
                        continue;
                     int id = target+(grid[r][c]-1 == target ? 1 : 0);
                     if(dist[r*N+c][piece][id] > dist[tempo][piece][target]+1)
                     {
                        replace[r*N+c][piece][id] = replace[tempo][piece][target];
                        dist[r*N+c][piece][id] = dist[tempo][piece][target]+1;
                        q.add(r*N+c);
                        q.add(piece);
                        q.add(id);
                     }
                     else if(dist[r*N+c][piece][id] == dist[tempo][piece][target]+1 && replace[r*N+c][piece][id] > replace[tempo][piece][target])
                     {
                        replace[r*N+c][piece][id] = replace[tempo][piece][target];
                        q.add(r*N+c);
                        q.add(piece);
                        q.add(id);
                     }
                  }
            }
         }
         int minDist = 420420420;
         int minRep = 420420420;
         int lmao = N*N;
         for(int p=0; p < 3; p++)
         {
            if(minDist > dist[loc[lmao]][p][lmao])
            {
               minDist = dist[loc[lmao]][p][lmao];
               minRep = replace[loc[lmao]][p][lmao];
            }
            else if(minDist == dist[loc[lmao]][p][lmao] && minRep > replace[loc[lmao]][p][lmao])
               minRep = replace[loc[lmao]][p][lmao];
         }
         System.out.println(minDist+" "+minRep);
      }
   }

import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.io.BufferedWriter;
import java.util.Collection;
import java.util.InputMismatchException;
import java.io.IOException;
import java.util.TreeSet;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.util.Queue;
import java.util.Comparator;
import java.util.ArrayDeque;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author t.me/musin_acm
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        OutputWriter out = new OutputWriter(outputStream);
        D solver = new D();
        solver.solve(1, in, out);
        out.close();
    }

    static class D {
        int[][][][][] distanceByType;
        int n;
        int[][] pos;
        int A = 0;
        int B = 1;
        int C = 2;
        int[][][] turns;

        public void solve(int testNumber, InputReader in, OutputWriter out) {
            n = in.readInt();
            pos = new int[n * n][2];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    int x = in.readInt() - 1;
                    pos[x][0] = i;
                    pos[x][1] = j;
                }
            }
            initAll();
            IntIntPair ans = solve();
            out.print(ans.first, ans.second);
        }

        IntIntPair solve() {
            int[][][] dp = new int[n * n][3][20 * 100];
            ArrayUtils.fill(dp, Integer.MAX_VALUE);
            dp[0][0][0] = dp[0][1][0] = dp[0][2][0] = 0;
            for (int i = 0; i + 1 < n * n; i++) {
                int x0 = pos[i][0];
                int y0 = pos[i][1];
                int x1 = pos[i + 1][0];
                int y1 = pos[i + 1][1];
                for (int t0 = 0; t0 < 3; t0++) {
                    IntIntPair[] res = go(x0, y0, t0, x1, y1);
                    for (int t1 = 0; t1 < 3; t1++) {
                        if (res[t1] == null) {
                            continue;
                        }
                        for (int prevRes = 0; prevRes < 20 * 100; prevRes++) {
                            if (dp[i][t0][prevRes] == Integer.MAX_VALUE) {
                                continue;
                            }
                            dp[i + 1][t1][prevRes + res[t1].first] = Math.min(
                                    dp[i + 1][t1][prevRes + res[t1].first],
                                    dp[i][t0][prevRes] + res[t1].second
                            );
                        }
                    }
                }
            }
            IntIntPair ans = IntIntPair.makePair(Integer.MAX_VALUE, Integer.MAX_VALUE);
            for (int res = 0; res < 20 * 100; res++) {
                for (int t = 0; t < 3; t++) {
                    if (dp[n * n - 1][t][res] == Integer.MAX_VALUE) {
                        continue;
                    }
                    IntIntPair cur = IntIntPair.makePair(res, dp[n * n - 1][t][res]);
                    if (cur.compareTo(ans) < 0) {
                        ans = cur;
                    }
                }
            }
            return ans;
        }

        IntIntPair[] go(int x0, int y0, int t0, int x1, int y1) {

            IntIntPair[][][] dp = new IntIntPair[n][n][3];

            TreeSet<Pair<IntIntPair, Integer>> q = new TreeSet<>(Comparator
                    .<Pair<IntIntPair, Integer>, IntIntPair>comparing(
                            p1 -> dp[p1.first.first][p1.first.second][p1.second])
                    .thenComparing(p1 -> p1));
            dp[x0][y0][t0] = IntIntPair.makePair(0, 0);
            q.add(Pair.makePair(IntIntPair.makePair(x0, y0), t0));
            while (!q.isEmpty()) {
                Pair<IntIntPair, Integer> cur = q.pollFirst();
                int curX = cur.first.first;
                int curY = cur.first.second;
                int curT = cur.second;
                IntIntPair curRes = dp[curX][curY][curT];
                for (int t = 0; t < 3; t++) {
                    int[][] turns = this.turns[t];
                    int addT = t == curT ? 0 : 1;
                    for (int[] turn : turns) {
                        int x = curX + turn[0];
                        int y = curY + turn[1];
                        if (inside(x, y)) {
                            IntIntPair p = IntIntPair.makePair(curRes.first + 1 + addT, curRes.second + addT);
                            if (dp[x][y][t] == null || better(p, dp[x][y][t])) {
                                Pair<IntIntPair, Integer> curP = Pair.makePair(IntIntPair.makePair(x, y), t);
                                if (dp[x][y][t] != null) {
                                    q.remove(curP);
                                }
                                dp[x][y][t] = p;
                                q.add(curP);
                            }
                        }
                    }
                }
            }
            return dp[x1][y1];
        }

        boolean better(IntIntPair p1, IntIntPair p2) {
            return p1.compareTo(p2) < 0;
        }

        void initAll() {
            initTurns();
            initDistances();
        }

        void initDistances() {
            distanceByType = new int[3][][][][];
            for (int i = 0; i < 3; i++) {
                distanceByType[i] = goBySingleType(i);
            }
        }

        int[][][][] goBySingleType(int type) {
            int[][][][] dp = new int[n][n][][];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    dp[i][j] = goBySingleType(type, i, j);
                }
            }
            return dp;
        }

        int[][] goBySingleType(int type, int x0, int y0) {
            int[][] dp = new int[n][n];
            ArrayUtils.fill(dp, -1);
            dp[x0][y0] = 0;
            Queue<IntIntPair> q = new ArrayDeque<>();
            q.add(IntIntPair.makePair(x0, y0));
            while (!q.isEmpty()) {
                IntIntPair cur = q.poll();
                for (int[] turn : turns[type]) {
                    int x = cur.first + turn[0];
                    int y = cur.second + turn[1];
                    if (inside(x, y) && dp[x][y] == -1) {
                        dp[x][y] = dp[cur.first][cur.second] + 1;
                        q.add(IntIntPair.makePair(x, y));
                    }
                }
            }
            return dp;
        }

        boolean inside(int x, int y) {
            return 0 <= x && x < n && 0 <= y && y < n;
        }

        void initTurns() {
            turns = new int[3][][];
            turns[A] = turnsA();
            turns[B] = turnsB();
            turns[C] = turnsC();
        }

        int[][] turnsA() {
            int[] dx = {-1, -2, -2, -1, 1, 2, 2, 1};
            int[] dy = {-2, -1, 1, 2, 2, 1, -1, -2};
            int[][] d = new int[8][2];
            for (int i = 0; i < 8; i++) {
                d[i][0] = dx[i];
                d[i][1] = dy[i];
            }
            return d;
        }

        int[][] turnsB() {
            int[][] t = new int[2 * (2 * n - 1)][2];
            int at = 0;
            for (int i = 0, x = -n + 1, y = -n + 1; i < 2 * n - 1; i++, at++) {
                t[at][0] = x;
                t[at][1] = y;
                x += 1;
                y += 1;
            }
            for (int i = 0, x = -n + 1, y = n - 1; i < 2 * n - 1; i++, at++) {
                t[at][0] = x;
                t[at][1] = y;
                x += 1;
                y -= 1;
            }
            return t;
        }

        int[][] turnsC() {
            int[][] t = new int[2 * (2 * n - 1)][2];
            int at = 0;
            for (int i = 0, x = -n + 1, y = 0; i < 2 * n - 1; i++, at++) {
                t[at][0] = x;
                t[at][1] = y;
                x += 1;
            }
            for (int i = 0, x = 0, y = -n + 1; i < 2 * n - 1; i++, at++) {
                t[at][0] = x;
                t[at][1] = y;
                y += 1;
            }
            return t;
        }

    }

    static class OutputWriter {
        private final PrintWriter writer;

        public OutputWriter(OutputStream outputStream) {
            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
        }

        public OutputWriter(Writer writer) {
            this.writer = new PrintWriter(writer);
        }

        public void print(Object... objects) {
            for (int i = 0; i < objects.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(objects[i]);
            }
        }

        public void close() {
            writer.close();
        }

    }

    static class InputReader {
        private InputStream stream;
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;
        private InputReader.SpaceCharFilter filter;

        public InputReader(InputStream stream) {
            this.stream = stream;
        }

        public int read() {
            if (numChars == -1) {
                throw new InputMismatchException();
            }
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (numChars <= 0) {
                    return -1;
                }
            }
            return buf[curChar++];
        }

        public int readInt() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9') {
                    throw new InputMismatchException();
                }
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public boolean isSpaceChar(int c) {
            if (filter != null) {
                return filter.isSpaceChar(c);
            }
            return isWhitespace(c);
        }

        public static boolean isWhitespace(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        public interface SpaceCharFilter {
            public boolean isSpaceChar(int ch);

        }

    }

    static class ArrayUtils {
        public static void fill(int[][] array, int value) {
            for (int[] row : array) {
                Arrays.fill(row, value);
            }
        }

        public static void fill(int[][][] array, int value) {
            for (int[][] subArray : array) {
                fill(subArray, value);
            }
        }

    }

    static class IntIntPair implements Comparable<IntIntPair> {
        public final int first;
        public final int second;

        public static IntIntPair makePair(int first, int second) {
            return new IntIntPair(first, second);
        }

        public IntIntPair(int first, int second) {
            this.first = first;
            this.second = second;
        }

        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            IntIntPair pair = (IntIntPair) o;

            return first == pair.first && second == pair.second;
        }

        public int hashCode() {
            int result = first;
            result = 31 * result + second;
            return result;
        }

        public String toString() {
            return "(" + first + "," + second + ")";
        }

        public int compareTo(IntIntPair o) {
            int value = Integer.compare(first, o.first);
            if (value != 0) {
                return value;
            }
            return Integer.compare(second, o.second);
        }

    }

    static class Pair<U, V> implements Comparable<Pair<U, V>> {
        public final U first;
        public final V second;

        public static <U, V> Pair<U, V> makePair(U first, V second) {
            return new Pair<U, V>(first, second);
        }

        private Pair(U first, V second) {
            this.first = first;
            this.second = second;
        }

        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            Pair pair = (Pair) o;

            return !(first != null ? !first.equals(pair.first) : pair.first != null) &&
                    !(second != null ? !second.equals(pair.second) : pair.second != null);
        }

        public int hashCode() {
            int result = first != null ? first.hashCode() : 0;
            result = 31 * result + (second != null ? second.hashCode() : 0);
            return result;
        }

        public String toString() {
            return "(" + first + "," + second + ")";
        }

        public int compareTo(Pair<U, V> o) {
            int value = ((Comparable<U>) first).compareTo(o.first);
            if (value != 0) {
                return value;
            }
            return ((Comparable<V>) second).compareTo(o.second);
        }

    }
}

import java.util.*;
import java.io.*;

public class Solution{
	static class FastReader
    {
        BufferedReader br;
        StringTokenizer st;

        public FastReader()
        {
            br = new BufferedReader(new
                     InputStreamReader(System.in));
        }

        String next()
        {
            while (st == null || !st.hasMoreElements())
            {
                try
                {
                    st = new StringTokenizer(br.readLine());
                }
                catch (IOException  e)
                {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt()
        {
            return Integer.parseInt(next());
        }

        long nextLong()
        {
            return Long.parseLong(next());
        }

        double nextDouble()
        {
            return Double.parseDouble(next());
        }

        String nextLine()
        {
            String str = "";
            try
            {
                str = br.readLine();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            return str;
        }
    }

	static class Node{
		int x,y,val,st,rep, type;
		public Node(int a1, int a2, int a3, int a4, int a5, int a6){
			val=a1; x=a2; y=a3; rep=a4; st=a5; type=a6;
		}

		public int getR(){
			return this.rep;
		}

		public int getSt(){
			return this.st;
		}
	}

	static public void main(String[] args){
		FastReader sc = new FastReader();

		int n = sc.nextInt();
		PriorityQueue<Node> q = new PriorityQueue<Node>(Comparator.comparingInt(Node::getSt).thenComparingInt(Node::getR));

		boolean vis[][][][] = new boolean[n*n+1][n+1][n+1][3];
		int[][][] move = new int[3][][];

		move[0] = new int[][]{{1,1}, {1,-1}, {-1,1}, {-1,-1}};
		move[1] = new int[][]{{0,1}, {1,0}, {-1,0}, {0,-1}};
		move[2] = new int[][]{{2,1}, {-1,-2}, {1,2}, {-1,2}, {1,-2}, {-2,1}, {-2,-1}, {2,-1} };

		int[][]pos = new int[n*n+1][2];

		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				pos[sc.nextInt()] = new int[]{i,j};

		q.add(new Node(1, pos[1][0], pos[1][1], 0, 0, 0));
		q.add(new Node(1, pos[1][0], pos[1][1], 0, 0, 1));
		q.add(new Node(1, pos[1][0], pos[1][1], 0, 0, 2));

		while(q.size()>0){
			Node cur = q.poll();
			if(cur.x<=0 || cur.x>n || cur.y<=0 || cur.y>n)	continue;

			if(vis[cur.val][cur.x][cur.y][cur.type]) continue;

			vis[cur.val][cur.x][cur.y][cur.type] = true;

			if(cur.x == pos[cur.val+1][0] && cur.y == pos[cur.val+1][1]){
				cur.val++;
				if(cur.val==n*n){
					System.out.println(cur.st+" "+cur.rep);
					return;
				}
				vis[cur.val][cur.x][cur.y][cur.type] = true;
			}

			for(int i=0;i<3;i++)
				q.add(new Node(cur.val, cur.x, cur.y, cur.rep+1, cur.st+1, i));

			for(int[] m : move[cur.type]){
				if(cur.type==2)
					q.add(new Node(cur.val, cur.x+m[0], cur.y+m[1], cur.rep, cur.st+1, cur.type));
				else
					for(int i=1;i<=n;i++)
						q.add(new Node(cur.val, cur.x+m[0]*i, cur.y+m[1]*i, cur.rep, cur.st+1, cur.type));
			}
		}

	}
}

import java.util.*;
import java.io.*;

public class Main {
	public static void main(String args[]) {new Main().run();}

	FastReader in = new FastReader();
	PrintWriter out = new PrintWriter(System.out);
	void run(){
		work();
		out.flush();
	}
	long mod=1000000007;
	long inf=Long.MAX_VALUE;
	long gcd(long a,long b) {
		return a==0?b:gcd(b%a,a);
	}
	int n;
	int[][] dir=new int[][] {{1,2},{2,1},{-1,2},{-2,1},{1,-2},{2,-1},{-1,-2},{-2,-1}};
	void work() {
		n=ni();
		int[][] rec=new int[n*n][];
		for(int i=0;i<n;i++) {
			for(int j=0;j<n;j++) {
				rec[ni()-1]=new int[] {i,j};
			}
		}
		int[][] dp=new int[3][2];//车相马,step replacement
		for(int i=0;i<n*n-1;i++) {
			int[][] ndp=new int[3][2];
			for(int[] d:ndp) {
				Arrays.fill(d, 99999999);
			}
			for(int j=0;j<3;j++) {
				int[][] A=bfs(rec[i],rec[i+1],j);
				for(int k=0;k<3;k++) {
					int v1=dp[j][0]+A[k][0];
					int v2=dp[j][1]+A[k][1];
					if(v1<ndp[k][0]||(v1==ndp[k][0]&&v2<ndp[k][1])) {
						ndp[k][0]=v1;
						ndp[k][1]=v2;
					}
				}
			}
			dp=ndp;
		}
		int s=99999999,r=99999999;
		for(int[] d:dp) {
			if(d[0]<s) {
				s=d[0];
				r=d[1];
			}else if(d[0]==s&&d[1]<r) {
				r=d[1];
			}
		}
		out.println(s+" "+r);
	}

	private int[][] bfs(int[] start, int[] end, int state) {
		int[][][][] rec=new int[n][n][3][2];
		for(int i=0;i<n;i++) {
			for(int j=0;j<n;j++) {
				for(int k=0;k<3;k++) {
					rec[i][j][k][0]=99999999;
					rec[i][j][k][1]=99999999;
				}
			}
		}
		rec[start[0]][start[1]][state][0]=0;
		rec[start[0]][start[1]][state][1]=0;
		LinkedList<int[]> queue=new LinkedList<>();
		queue.add(new int[] {start[0],start[1],0,0,state});
		while(queue.size()>0) {
			int[] q=queue.poll();
			int x=q[0],y=q[1],step=q[2],rep=q[3],s=q[4];
			for(int i=0;i<3;i++) {
				int nstep=step+1;
				int nrep=rep+1;
				if(rec[x][y][i][0]>nstep||(rec[x][y][i][0]==nstep&&rec[x][y][i][1]>nrep)) {
					rec[x][y][i][0]=nstep;
					rec[x][y][i][1]=nrep;
					queue.add(new int[] {x,y,nstep,nrep,i});
				}
			}
			if(s==0) {
				for(int i=0;i<n;i++) {
					int nstep=step+1;
					if(rec[i][y][s][0]>nstep||(rec[i][y][s][0]==nstep&&rec[i][y][s][1]>rep)) {
						rec[i][y][s][0]=nstep;
						rec[i][y][s][1]=rep;
						queue.add(new int[] {i,y,nstep,rep,s});
					}
					if(rec[x][i][s][0]>nstep||(rec[x][i][s][0]==nstep&&rec[x][i][s][1]>rep)) {
						rec[x][i][s][0]=nstep;
						rec[x][i][s][1]=rep;
						queue.add(new int[] {x,i,nstep,rep,s});
					}
				}
			}else if(s==1) {
				for(int i=-n;i<n;i++) {
					int nstep=step+1;
					int nx=x+i,ny=y+i;
					if(nx>=0&&nx<n&&ny>=0&&ny<n) {
						if(rec[nx][ny][s][0]>nstep||(rec[nx][ny][s][0]==nstep&&rec[nx][ny][s][1]>rep)) {
							queue.add(new int[] {nx,ny,nstep,rep,s});
							rec[nx][ny][s][0]=nstep;
							rec[nx][ny][s][1]=rep;
						}
					}
					nx=x-i;ny=y+i;
					if(nx<0||nx>=n||ny<0||ny>=n)continue;
					if(rec[nx][ny][s][0]>nstep||(rec[nx][ny][s][0]==nstep&&rec[nx][ny][s][1]>rep)) {
						queue.add(new int[] {nx,ny,nstep,rep,s});
						rec[nx][ny][s][0]=nstep;
						rec[nx][ny][s][1]=rep;
					}
				}
			}else {
				for(int i=0;i<8;i++) {
					int nx=x+dir[i][0],ny=y+dir[i][1];
					int nstep=step+1;
					if(nx<0||nx>=n||ny<0||ny>=n)continue;
					if(rec[nx][ny][s][0]>nstep||(rec[nx][ny][s][0]==nstep&&rec[nx][ny][s][1]>rep)) {
						queue.add(new int[] {nx,ny,nstep,rep,s});
						rec[nx][ny][s][0]=nstep;
						rec[nx][ny][s][1]=rep;
					}
				}
			}
		}
		return rec[end[0]][end[1]];
	}

	//input
	@SuppressWarnings("unused")
	private ArrayList<Integer>[] ng(int n, int m) {
		ArrayList<Integer>[] graph=(ArrayList<Integer>[])new ArrayList[n];
		for(int i=0;i<n;i++) {
			graph[i]=new ArrayList<>();
		}
		for(int i=1;i<=m;i++) {
			int s=in.nextInt()-1,e=in.nextInt()-1;
			graph[s].add(e);
			graph[e].add(s);
		}
		return graph;
	}

	private ArrayList<long[]>[] ngw(int n, int m) {
		ArrayList<long[]>[] graph=(ArrayList<long[]>[])new ArrayList[n];
		for(int i=0;i<n;i++) {
			graph[i]=new ArrayList<>();
		}
		for(int i=1;i<=m;i++) {
			long s=in.nextLong()-1,e=in.nextLong()-1,w=in.nextLong();
			graph[(int)s].add(new long[] {e,w,i});
			graph[(int)e].add(new long[] {s,w});
		}
		return graph;
	}

	private int ni() {
		return in.nextInt();
	}

	private long nl() {
		return in.nextLong();
	}

	private String ns() {
		return in.next();
	}

	private long[] na(int n) {
		long[] A=new long[n];
		for(int i=0;i<n;i++) {
			A[i]=in.nextLong();
		}
		return A;
	}
	private int[] nia(int n) {
		int[] A=new int[n];
		for(int i=0;i<n;i++) {
			A[i]=in.nextInt();
		}
		return A;
	}
}

class FastReader
{
	BufferedReader br;
	StringTokenizer st;

	public FastReader()
	{
		br=new BufferedReader(new InputStreamReader(System.in));
	}

	public String next()
	{
		while(st==null || !st.hasMoreElements())//回车，空行情况
		{
			try {
				st = new StringTokenizer(br.readLine());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return st.nextToken();
	}

	public int nextInt()
	{
		return Integer.parseInt(next());
	}

	public long nextLong()
	{
		return Long.parseLong(next());
	}
}

import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author Vadim
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        TaskD solver = new TaskD();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskD {
        public static final int MAX_PATH = 1000000;
        public static final int K = 0;
        public static final int B = 1;
        public static final int R = 2;
        Point[] coords;
        int[][] d;
        int[] di = new int[]{2, 2, 1, -1, -2, -2, 1, -1};
        int[] dj = new int[]{-1, 1, 2, 2, 1, -1, -2, -2};
        int n;

        int getBDist(int r, int c, int ra, int ca) {
            if ((r + c) % 2 != (ra + ca) % 2)
                return MAX_PATH;
            if (r - ra == c - ca || r - ra == ca - c)
                return 1;
            return 2;
        }

        int getRDist(int r, int c, int ra, int ca) {
            if (r == ra || c == ca)
                return 1;
            return 2;
        }

        Pair dist(int from, int to, int start, int end) {
            Point p = coords[from];
            Point q = coords[to];
            if (start == end) {
                if (start == K)
                    return new Pair(d[from][to], 0);
                else if (start == B) {
                    return new Pair(getBDist(p.r, p.c, q.r, q.c), 0);//todo:
                } else
                    return new Pair(getRDist(p.r, p.c, q.r, q.c), 0);
            } else {
                if (start == R && end == B || start == B && end == R) {
                    if (getBDist(p.r, p.c, q.r, q.c) == 1)
                        return new Pair(2, 1);
                    if (getRDist(p.r, p.c, q.r, q.c) == 1)
                        return new Pair(2, 1);
                    return new Pair(3, 1);
                }
                if (start == K && end == B || start == B && end == K) {
                    if (end == K) {
                        Point t = p;
                        p = q;
                        q = t;
                    }
                    int min = getBDist(p.r, p.c, q.r, q.c);
                    for (int i = 0; i < 8; i++) {
                        int ni = p.r + di[i];
                        int nj = p.c + dj[i];
                        if (0 <= ni && ni < n && 0 <= nj && nj < n) {
                            min = Math.min(1 + getBDist(ni, nj, q.r, q.c), min);
                        }
                    }
                    return new Pair(min + 1, 1);
                }
                if (start == K && end == R || start == R && end == K) {
                    if (end == K) {
                        Point t = p;
                        p = q;
                        q = t;
                    }
                    int min = getRDist(p.r, p.c, q.r, q.c);
                    for (int i = 0; i < 8; i++) {
                        int ni = p.r + di[i];
                        int nj = p.c + dj[i];
                        if (0 <= ni && ni < n && 0 <= nj && nj < n) {
                            min = Math.min(1 + getRDist(ni, nj, q.r, q.c), min);
                        }
                    }
                    return new Pair(min + 1, 1);
                }
            }
            throw new RuntimeException();

        }

        public void solve(int testNumber, InputReader in, PrintWriter out) {

            n = in.ni();
            int[][] a = new int[n][n];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    a[i][j] = in.ni() - 1;
                }
            }
            int n2 = n * n;

            d = new int[n2][n2];
            for (int i = 0; i < n2; i++) {
                for (int j = 0; j < n2; j++) {
                    if (i == j)
                        d[i][i] = 0;
                    else
                        d[i][j] = MAX_PATH;
                }
            }

            coords = new Point[n2];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    coords[a[i][j]] = new Point(i, j);
                    for (int k = 0; k < 8; k++) {
                        int ni = i + di[k];
                        int nj = j + dj[k];
                        if (0 <= ni && ni < n && 0 <= nj && nj < n) {
                            d[a[i][j]][a[ni][nj]] = 1;
                        }
                    }

                }
            }
            for (int k = 0; k < n2; k++) {
                for (int i = 0; i < n2; i++) {
                    for (int j = 0; j < n2; j++) {
                        d[i][j] = Math.min(d[i][j], d[i][k] + d[k][j]);
                    }
                }
            }
            int ans[][] = new int[n2][3];
            int swaps[][] = new int[n2][3];
            for (int i = 0; i < n2 - 1; i++) {
                for (int j = 0; j < 3; j++) {
                    int minDist = MAX_PATH;
                    int minSwap = n2;
                    for (int k = 0; k < 3; k++) {
                        Pair pair = dist(i, i + 1, k, j);
                        int dist = ans[i][k] + pair.x;
                        int swap = swaps[i][k] + pair.y;
                        if (dist < minDist || dist == minDist && swap < minSwap) {
                            minDist = dist;
                            minSwap = swap;
                        }
                    }
                    ans[i + 1][j] += minDist;
                    swaps[i + 1][j] = minSwap;
                }
            }
            int minDist = MAX_PATH;
            int minSwap = n2;
            for (int k = 0; k < 3; k++) {
                int dist = ans[n2 - 1][k];
                int swap = swaps[n2 - 1][k];
                if (dist < minDist || dist == minDist && swap < minSwap) {
                    minDist = dist;
                    minSwap = swap;
                }
            }

            out.println(minDist + " " + minSwap);

        }

        class Point {
            int r;
            int c;

            public Point(int r, int c) {
                this.r = r;
                this.c = c;
            }

        }

        class Pair {
            int x;
            int y;

            public Pair(int x, int y) {
                this.x = x;
                this.y = y;
            }

        }

    }

    static class InputReader extends BufferedReader {
        public InputReader(InputStream st) {
            super(new InputStreamReader(st));
        }

        public String readLine() {
            try {
                return super.readLine();
            } catch (IOException e) {
                return null;
            }
        }

        private int readByte() {
            try {
                return read();
            } catch (IOException e) {
                throw new RuntimeException();
            }
        }

        public int ni() {
            int num = 0, b;
            boolean minus = false;
            while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-')) ;
            if (b == '-') {
                minus = true;
                b = readByte();
            }

            while (true) {
                if (b >= '0' && b <= '9') {
                    num = num * 10 + (b - '0');
                } else {
                    return minus ? -num : num;
                }
                b = readByte();
            }
        }

    }
}

import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.PrintStream;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author Vadim
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        TaskD solver = new TaskD();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskD {
        public static final int MAX_PATH = 1000000;
        public static final int K = 0;
        public static final int B = 1;
        public static final int R = 2;

        static int getBDist(int r, int c, int ra, int ca) {
            if ((r + c) % 2 != (ra + ca) % 2)
                return MAX_PATH;
            if (r - ra == c - ca || r - ra == ca - c)
                return 1;
            return 2;
        }

        static int getRDist(int r, int c, int ra, int ca) {
            if (r == ra || c == ca)
                return 1;
            return 2;
        }

        public void solve(int testNumber, InputReader in, PrintWriter out) {
            int n = in.ni();
            int[][] a = new int[n][n];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    a[i][j] = in.ni() - 1;
                }
            }
            int n2 = n * n;
            int[][] d = new int[n2][n2];
            for (int i = 0; i < n2; i++) {
                for (int j = 0; j < n2; j++) {
                    if (i == j)
                        d[i][i] = 0;
                    else
                        d[i][j] = MAX_PATH;
                }
            }
            int[] di = new int[]{2, 2, 1, -1, -2, -2, 1, -1};
            int[] dj = new int[]{-1, 1, 2, 2, 1, -1, -2, -2};
            Point[] coords = new Point[n2];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    coords[a[i][j]] = new Point(i, j);
                    for (int k = 0; k < 8; k++) {
                        int ni = i + di[k];
                        int nj = j + dj[k];
                        if (0 <= ni && ni < n && 0 <= nj && nj < n) {
                            d[a[i][j]][a[ni][nj]] = 1;
                        }
                    }

                }
            }
            for (int k = 0; k < n2; k++) {
                for (int i = 0; i < n2; i++) {
                    for (int j = 0; j < n2; j++) {
                        d[i][j] = Math.min(d[i][j], d[i][k] + d[k][j]);
                    }
                }
            }
            int ans[][] = new int[n2][3];
            int swaps[][] = new int[n2][3];
            for (int i = 0; i < n2 - 1; i++) {
                Point p = coords[i];
                Point next = coords[i + 1];
                ans[i + 1][K] = d[i][i + 1];
                ans[i + 1][B] = getBDist(p.r, p.c, next.r, next.c);
                ans[i + 1][R] = getRDist(p.r, p.c, next.r, next.c);
                for (int j = 0; j < 3; j++) {
                    int minDist = MAX_PATH;
                    int minSwap = n2;
                    for (int k = 0; k < 3; k++) {
                        int dist = ans[i][k];
                        int swap = swaps[i][k];
                        if (j != k) {
                            dist++;
                            swap++;
                        }
                        if (dist < minDist || dist == minDist && swap < minSwap) {
                            minDist = dist;
                            minSwap = swap;
                        }
                    }
                    ans[i + 1][j] += minDist;
                    swaps[i + 1][j] = minSwap;
                }
            }
            int minDist = MAX_PATH;
            int minSwap = n2;
            for (int k = 0; k < 3; k++) {
                int dist = ans[n2 - 1][k];
                int swap = swaps[n2 - 1][k];
                if (dist < minDist || dist == minDist && swap < minSwap) {
                    minDist = dist;
                    minSwap = swap;
                }
            }

            System.out.println(minDist + " " + minSwap);

        }

        class Point {
            int r;
            int c;

            public Point(int r, int c) {
                this.r = r;
                this.c = c;
            }

        }

    }

    static class InputReader extends BufferedReader {
        public InputReader(InputStream st) {
            super(new InputStreamReader(st));
        }

        public String readLine() {
            try {
                return super.readLine();
            } catch (IOException e) {
                return null;
            }
        }

        private int readByte() {
            try {
                return read();
            } catch (IOException e) {
                throw new RuntimeException();
            }
        }

        public int ni() {
            int num = 0, b;
            boolean minus = false;
            while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-')) ;
            if (b == '-') {
                minus = true;
                b = readByte();
            }

            while (true) {
                if (b >= '0' && b <= '9') {
                    num = num * 10 + (b - '0');
                } else {
                    return minus ? -num : num;
                }
                b = readByte();
            }
        }

    }
}

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.*;

public class d{

	static class Node{
		int i, j, from, to, cost;
		Node(int i, int j, int from, int to, int cost){this.i=i;this.j=j;this.from=from;this.to=to;this.cost=cost;}
	}

	static class Node2{
		int i, j, type, cost, n;
		Node2(int i, int j, int t, int c, int n){this.i=i;this.j=j;type=t;cost=c;this.n=n;}
	}

	static class Pos{
		int i, j;
		Pos(int i, int j){this.i=i;this.j=j;}
	}

	static void solve(){
		int n = ni();
		int[][][][] dis = new int[n][n][3][3];
		for(int i=0;i<n;++i)for(int j=0;j<n;++j)for(int k=0;k<3;++k)for(int l=0;l<3;++l)dis[i][j][k][l]=1000000;
		PriorityQueue<Node> que = new PriorityQueue<>((a,b)->a.cost-b.cost);
		for(int i=0;i<3;++i){
			dis[0][0][i][i]=0;
			que.add(new Node(0,0,i,i, 0));
		}
		int[] kx = {-2, -2, -1, -1, 1, 1, 2, 2};
		int[] ky = {1, -1, 2, -2, 2, -2, 1, -1};

		while(!que.isEmpty()){
			Node node = que.poll();
			if(dis[node.i][node.j][node.from][node.to]<node.cost)continue;
			for(int i=0;i<3;++i){
				if(i==node.to)continue;
				if(dis[node.i][node.j][node.from][i] > node.cost+1001){
					dis[node.i][node.j][node.from][i]=node.cost+1001;
					que.add(new Node(node.i, node.j, node.from, i, node.cost+1001));
				}
			}
			if(node.to==0){
				for(int k=0;k<kx.length;++k){
					int ni = node.i+kx[k], nj = node.j+ky[k];
					if(inrange(node.i+kx[k], node.j+ky[k], n, n) && dis[node.i+kx[k]][node.j+ky[k]][node.from][node.to] > node.cost+1000){
						dis[ni][nj][node.from][node.to]=node.cost+1000;
						que.add(new Node(ni, nj, node.from, node.to, node.cost+1000));
					}
				}
			}else if(node.to==1){
				for(int k=0;k<n;++k){
					int ni = k, nj=node.j;
					if(dis[ni][nj][node.from][node.to] > node.cost+1000){
						dis[ni][nj][node.from][node.to]=node.cost+1000;
						que.add(new Node(ni, nj, node.from, node.to, node.cost+1000));
					}
				}
				for(int k=0;k<n;++k){
					int ni = node.i, nj=k;
					if(dis[ni][nj][node.from][node.to] > node.cost+1000){
						dis[ni][nj][node.from][node.to]=node.cost+1000;
						que.add(new Node(ni, nj, node.from, node.to, node.cost+1000));
					}
				}
			}else{
				for(int k=-n;k<=n;++k){
					int ni = node.i+k, nj=node.j+k;
					if(inrange(ni, nj, n, n) && dis[ni][nj][node.from][node.to] > node.cost+1000){
						dis[ni][nj][node.from][node.to]=node.cost+1000;
						que.add(new Node(ni, nj, node.from, node.to, node.cost+1000));
					}
					nj = node.j-k;
					if(inrange(ni, nj, n, n) && dis[ni][nj][node.from][node.to] > node.cost+1000){
						dis[ni][nj][node.from][node.to]=node.cost+1000;
						que.add(new Node(ni, nj, node.from, node.to, node.cost+1000));
					}
				}
			}
		}
		PriorityQueue<Node2> que2 = new PriorityQueue<>((a,b)->a.cost-b.cost);
		int[][][] dis2 = new int[n][n][3];
		for(int i=0;i<n;++i)for(int j=0;j<n;++j)for(int k=0;k<3;++k)dis2[i][j][k]=1000000000;

		Pos[] pos = new Pos[n*n];
		for(int i=0;i<n;++i)for(int j=0;j<n;++j){
			pos[ni()-1] = new Pos(i, j);
		}

		for(int i=0;i<3;++i){
			dis2[pos[0].i][pos[0].j][i]=0;
			que2.add(new Node2(pos[0].i, pos[0].j, i, 0, 0 ));
		}
		while(!que2.isEmpty()){
			Node2 node = que2.poll();
			if(dis2[node.i][node.j][node.type]<node.cost)continue;
			if(node.n == n*n-1){
				out.println(node.cost/1000+" "+node.cost%1000);
				return;
			}
			int ni = pos[node.n+1].i, nj = pos[node.n+1].j;
			for(int t=0;t<3;++t){
				int d = node.cost/1000;
				int c = node.cost%1000;
				d += dis[Math.abs(ni-node.i)][Math.abs(nj-node.j)][node.type][t]/1000;
				c += dis[Math.abs(ni-node.i)][Math.abs(nj-node.j)][node.type][t]%1000;
				int nc = d*1000+c;
				if(dis2[ni][nj][t] > nc){
					dis2[ni][nj][t] = nc;
					que2.add(new Node2(ni, nj, t, dis2[ni][nj][t], node.n+1));
				}
			}
		}

	}

	 public static void main(String[] args){
		 solve();
		 out.flush();
	 }
	 private static InputStream in = System.in;
	 private static PrintWriter out = new PrintWriter(System.out);

	 static boolean inrange(int y, int x, int h, int w){
		 return y>=0 && y<h && x>=0 && x<w;
	 }
	 @SuppressWarnings("unchecked")
	 static<T extends Comparable> int lower_bound(List<T> list, T key){
		 int lower=-1;int upper=list.size();
		 while(upper - lower>1){
		 int center =(upper+lower)/2;
		 if(list.get(center).compareTo(key)>=0)upper=center;
		 else lower=center;
		 }
		 return upper;
	 }
	 @SuppressWarnings("unchecked")
	 static <T extends Comparable> int upper_bound(List<T> list, T key){
		 int lower=-1;int upper=list.size();
		 while(upper-lower >1){
		 int center=(upper+lower)/2;
		 if(list.get(center).compareTo(key)>0)upper=center;
		 else lower=center;
		 }
		 return upper;
	 }
	 @SuppressWarnings("unchecked")
	 static <T extends Comparable> boolean next_permutation(List<T> list){
		 int lastIndex = list.size()-2;
		 while(lastIndex>=0 && list.get(lastIndex).compareTo(list.get(lastIndex+1))>=0)--lastIndex;
		 if(lastIndex<0)return false;
		 int swapIndex = list.size()-1;
		 while(list.get(lastIndex).compareTo(list.get(swapIndex))>=0)swapIndex--;
		 T tmp = list.get(lastIndex);
		 list.set(lastIndex++, list.get(swapIndex));
		 list.set(swapIndex, tmp);
		 swapIndex = list.size()-1;
		 while(lastIndex<swapIndex){
		 tmp = list.get(lastIndex);
		 list.set(lastIndex, list.get(swapIndex));
		 list.set(swapIndex, tmp);
		 ++lastIndex;--swapIndex;
		 }
		 return true;
	 }
	 private static final byte[] buffer = new byte[1<<15];
	 private static int ptr = 0;
	 private static int buflen = 0;
	 private static boolean hasNextByte(){
		 if(ptr<buflen)return true;
		 ptr = 0;
		 try{
			 buflen = in.read(buffer);
		 } catch (IOException e){
			 e.printStackTrace();
		 }
		 return buflen>0;
	 }
	 private static int readByte(){ if(hasNextByte()) return buffer[ptr++]; else return -1;}
	 private static boolean isSpaceChar(int c){ return !(33<=c && c<=126);}
	 private static int skip(){int res; while((res=readByte())!=-1 && isSpaceChar(res)); return res;}

	 private static double nd(){ return Double.parseDouble(ns()); }
	 private static char nc(){ return (char)skip(); }
	 private static String ns(){
		 StringBuilder sb = new StringBuilder();
		 for(int b=skip();!isSpaceChar(b);b=readByte())sb.append((char)b);
		 return sb.toString();
	 }
	 private static int[] nia(int n){
		 int[] res = new int[n];
		 for(int i=0;i<n;++i)res[i]=ni();
		 return res;
	 }
	 private static long[] nla(int n){
		 long[] res = new long[n];
		 for(int i=0;i<n;++i)res[i]=nl();
		 return res;
	 }
	 private static int ni(){
		 int res=0,b;
		 boolean minus=false;
		 while((b=readByte())!=-1 && !((b>='0'&&b<='9') || b=='-'));
		 if(b=='-'){
			 minus=true;
			 b=readByte();
		 }
		 for(;'0'<=b&&b<='9';b=readByte())res=res*10+(b-'0');
		 return minus ? -res:res;
	 }
	 private static long nl(){
		 long res=0,b;
		 boolean minus=false;
		 while((b=readByte())!=-1 && !((b>='0'&&b<='9') || b=='-'));
		 if(b=='-'){
			 minus=true;
			 b=readByte();
		 }
		 for(;'0'<=b&&b<='9';b=readByte())res=res*10+(b-'0');
		 return minus ? -res:res;
	}
}

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.*;

public class d{

	static class Node{
		int i, j, from, to, cost;
		Node(int i, int j, int from, int to, int cost){this.i=i;this.j=j;this.from=from;this.to=to;this.cost=cost;}
	}

	static class Node2{
		int i, j, type, cost, n;
		Node2(int i, int j, int t, int c, int n){this.i=i;this.j=j;type=t;cost=c;this.n=n;}
	}

	static class Pos{
		int i, j;
		Pos(int i, int j){this.i=i;this.j=j;}
	}

	static void solve(){
		int n = ni();
		int[][][][] dis = new int[n][n][3][3];
		for(int i=0;i<n;++i)for(int j=0;j<n;++j)for(int k=0;k<3;++k)for(int l=0;l<3;++l)dis[i][j][k][l]=1000000;
		PriorityQueue<Node> que = new PriorityQueue<>((a,b)->a.cost-b.cost);
		for(int i=0;i<3;++i){
			dis[0][0][i][i]=0;
			que.add(new Node(0,0,i,i, 0));
		}
		int[] kx = {-2, -2, -1, -1, 1, 1, 2, 2};
		int[] ky = {1, -1, 2, -2, 2, -2, 1, -1};

		while(!que.isEmpty()){
			Node node = que.poll();
			if(dis[node.i][node.j][node.from][node.to]<node.cost)continue;
			for(int i=0;i<3;++i){
				if(i==node.to)continue;
				if(dis[node.i][node.j][node.from][i] > node.cost+1){
					dis[node.i][node.j][node.from][i]=node.cost+1;
					que.add(new Node(node.i, node.j, node.from, i, node.cost+1));
				}
			}
			if(node.to==0){
				for(int k=0;k<kx.length;++k){
					int ni = node.i+kx[k], nj = node.j+ky[k];
					if(inrange(node.i+kx[k], node.j+ky[k], n, n) && dis[node.i+kx[k]][node.j+ky[k]][node.from][node.to] > node.cost+1){
						dis[ni][nj][node.from][node.to]=node.cost+1;
						que.add(new Node(ni, nj, node.from, node.to, node.cost+1));
					}
				}
			}else if(node.to==1){
				for(int k=0;k<n;++k){
					int ni = k, nj=node.j;
					if(dis[ni][nj][node.from][node.to] > node.cost+1){
						dis[ni][nj][node.from][node.to]=node.cost+1;
						que.add(new Node(ni, nj, node.from, node.to, node.cost+1));
					}
				}
				for(int k=0;k<n;++k){
					int ni = node.i, nj=k;
					if(dis[ni][nj][node.from][node.to] > node.cost+1){
						dis[ni][nj][node.from][node.to]=node.cost+1;
						que.add(new Node(ni, nj, node.from, node.to, node.cost+1));
					}
				}
			}else{
				for(int k=-n;k<=n;++k){
					int ni = node.i+k, nj=node.j+k;
					if(inrange(ni, nj, n, n) && dis[ni][nj][node.from][node.to] > node.cost+1){
						dis[ni][nj][node.from][node.to]=node.cost+1;
						que.add(new Node(ni, nj, node.from, node.to, node.cost+1));
					}
					nj = node.j-k;
					if(inrange(ni, nj, n, n) && dis[ni][nj][node.from][node.to] > node.cost+1){
						dis[ni][nj][node.from][node.to]=node.cost+1;
						que.add(new Node(ni, nj, node.from, node.to, node.cost+1));
					}
				}
			}
		}
		PriorityQueue<Node2> que2 = new PriorityQueue<>((a,b)->a.cost-b.cost);
		int[][][] dis2 = new int[n][n][3];
		for(int i=0;i<n;++i)for(int j=0;j<n;++j)for(int k=0;k<3;++k)dis2[i][j][k]=100000000;

		Pos[] pos = new Pos[n*n];
		for(int i=0;i<n;++i)for(int j=0;j<n;++j){
			pos[ni()-1] = new Pos(i, j);
		}

		for(int i=0;i<3;++i){
			dis2[pos[0].i][pos[0].j][i]=0;
			que2.add(new Node2(pos[0].i, pos[0].j, i, 0, 0 ));
		}
		while(!que2.isEmpty()){
			Node2 node = que2.poll();
			if(dis2[node.i][node.j][node.type]<node.cost)continue;
			if(node.n == n*n-1){
				out.println(node.cost/1000+" "+node.cost%1000);
				return;
			}
			int ni = pos[node.n+1].i, nj = pos[node.n+1].j;
			for(int t=0;t<3;++t){
				int d = node.cost/1000;
				int c = node.cost%1000;
				d += dis[Math.abs(ni-node.i)][Math.abs(nj-node.j)][node.type][t];
				if(t != node.type)c++;
				int nc = d*1000+c;
				if(dis2[ni][nj][t] > nc){
					dis2[ni][nj][t] = nc;
					que2.add(new Node2(ni, nj, t, dis2[ni][nj][t], node.n+1));
				}
			}
		}

	}

	 public static void main(String[] args){
		 solve();
		 out.flush();
	 }
	 private static InputStream in = System.in;
	 private static PrintWriter out = new PrintWriter(System.out);

	 static boolean inrange(int y, int x, int h, int w){
		 return y>=0 && y<h && x>=0 && x<w;
	 }
	 @SuppressWarnings("unchecked")
	 static<T extends Comparable> int lower_bound(List<T> list, T key){
		 int lower=-1;int upper=list.size();
		 while(upper - lower>1){
		 int center =(upper+lower)/2;
		 if(list.get(center).compareTo(key)>=0)upper=center;
		 else lower=center;
		 }
		 return upper;
	 }
	 @SuppressWarnings("unchecked")
	 static <T extends Comparable> int upper_bound(List<T> list, T key){
		 int lower=-1;int upper=list.size();
		 while(upper-lower >1){
		 int center=(upper+lower)/2;
		 if(list.get(center).compareTo(key)>0)upper=center;
		 else lower=center;
		 }
		 return upper;
	 }
	 @SuppressWarnings("unchecked")
	 static <T extends Comparable> boolean next_permutation(List<T> list){
		 int lastIndex = list.size()-2;
		 while(lastIndex>=0 && list.get(lastIndex).compareTo(list.get(lastIndex+1))>=0)--lastIndex;
		 if(lastIndex<0)return false;
		 int swapIndex = list.size()-1;
		 while(list.get(lastIndex).compareTo(list.get(swapIndex))>=0)swapIndex--;
		 T tmp = list.get(lastIndex);
		 list.set(lastIndex++, list.get(swapIndex));
		 list.set(swapIndex, tmp);
		 swapIndex = list.size()-1;
		 while(lastIndex<swapIndex){
		 tmp = list.get(lastIndex);
		 list.set(lastIndex, list.get(swapIndex));
		 list.set(swapIndex, tmp);
		 ++lastIndex;--swapIndex;
		 }
		 return true;
	 }
	 private static final byte[] buffer = new byte[1<<15];
	 private static int ptr = 0;
	 private static int buflen = 0;
	 private static boolean hasNextByte(){
		 if(ptr<buflen)return true;
		 ptr = 0;
		 try{
			 buflen = in.read(buffer);
		 } catch (IOException e){
			 e.printStackTrace();
		 }
		 return buflen>0;
	 }
	 private static int readByte(){ if(hasNextByte()) return buffer[ptr++]; else return -1;}
	 private static boolean isSpaceChar(int c){ return !(33<=c && c<=126);}
	 private static int skip(){int res; while((res=readByte())!=-1 && isSpaceChar(res)); return res;}

	 private static double nd(){ return Double.parseDouble(ns()); }
	 private static char nc(){ return (char)skip(); }
	 private static String ns(){
		 StringBuilder sb = new StringBuilder();
		 for(int b=skip();!isSpaceChar(b);b=readByte())sb.append((char)b);
		 return sb.toString();
	 }
	 private static int[] nia(int n){
		 int[] res = new int[n];
		 for(int i=0;i<n;++i)res[i]=ni();
		 return res;
	 }
	 private static long[] nla(int n){
		 long[] res = new long[n];
		 for(int i=0;i<n;++i)res[i]=nl();
		 return res;
	 }
	 private static int ni(){
		 int res=0,b;
		 boolean minus=false;
		 while((b=readByte())!=-1 && !((b>='0'&&b<='9') || b=='-'));
		 if(b=='-'){
			 minus=true;
			 b=readByte();
		 }
		 for(;'0'<=b&&b<='9';b=readByte())res=res*10+(b-'0');
		 return minus ? -res:res;
	 }
	 private static long nl(){
		 long res=0,b;
		 boolean minus=false;
		 while((b=readByte())!=-1 && !((b>='0'&&b<='9') || b=='-'));
		 if(b=='-'){
			 minus=true;
			 b=readByte();
		 }
		 for(;'0'<=b&&b<='9';b=readByte())res=res*10+(b-'0');
		 return minus ? -res:res;
	}
}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

public class Main {

    static int n;
    static int[] x;
    static int[] y;

    // 0代表马, 1代表象, 2代表车
    public static void main(String[] args) throws IOException {

        StreamTokenizer streamTokenizer = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        streamTokenizer.nextToken();
        n = (int) streamTokenizer.nval;
        x = new int[n * n + 1];
        y = new int[n * n + 1];
        int[][] f = new int[3][n * n + 1];
        int[][] g = new int[3][n * n + 1];
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++) {
                streamTokenizer.nextToken();
                x[(int) streamTokenizer.nval] = i;
                y[(int) streamTokenizer.nval] = j;
            }
        f[0][1] = 0;
        f[1][1] = 0;
        f[2][1] = 0;
        g[0][1] = 0;
        g[1][1] = 0;
        g[2][1] = 0;
        for (int i = 2; i <= n * n; i++) {
            for (int j = 0; j < 3; j++) {
                getMinStep(x[i - 1], y[i - 1], 0, x[i], y[i], j);
                f[j][i] = f[0][i - 1] + minStep;
                g[j][i] = g[0][i - 1] + minMove;
                getMinStep(x[i - 1], y[i - 1], 1, x[i], y[i], j);
                if ((f[1][i - 1] + minStep < f[j][i]) || ((f[1][i - 1] + minStep == f[j][i]) && (g[1][i - 1] + minMove < g[j][i]))) {
                    f[j][i] = f[1][i - 1] + minStep;
                    g[j][i] = g[1][i - 1] + minMove;
                }
                getMinStep(x[i - 1], y[i - 1], 2, x[i], y[i], j);
                if ((f[2][i - 1] + minStep < f[j][i]) || ((f[2][i - 1] + minStep == f[j][i]) && (g[2][i - 1] + minMove < g[j][i]))) {
                    f[j][i] = f[2][i - 1] + minStep;
                    g[j][i] = g[2][i - 1] + minMove;
                }
            }
        }
        minStep = f[0][n * n];
        minMove = g[0][n * n];

        if ((f[1][n * n] < minStep) || ((f[1][n * n] == minStep) && (g[1][n * n] < minMove))) {
            minStep = f[1][n * n];
            minMove = g[1][n * n];
        }
        if ((f[2][n * n] < minStep) || ((f[2][n * n] == minStep) && (g[2][n * n] < minMove))) {
            minStep = f[2][n * n];
            minMove = g[2][n * n];
        }
        System.out.print(minStep + " " + minMove);
    }

    static int[] pointX = new int[1000000];
    static int[] pointY = new int[1000000];
    static int[] state = new int[1000000];
    static int[] steps = new int[1000000];
    static int[] moves = new int[1000000];

    static int[][] knight = {
            {-2, -1},
            {-2, 1},
            {-1, -2},
            {-1, 2},
            {1, -2},
            {1, 2},
            {2, -1},
            {2, 1}
    };

    static int minStep;
    static int minMove;

    // 0代表马, 1代表象, 2代表车
    public static void getMinStep(int fromX, int fromY, int fromState, int toX, int toY, int toState) {

        int l = 0, r = 0;
        int tempX, tempY;
        pointX[0] = fromX;
        pointY[0] = fromY;
        state[0] = fromState;
        steps[0] = 0;
        moves[0] = 0;
        while (l <= r) {
            if (state[l] == 0) {
                for (int i = 0; i < knight.length; i++) {
                    tempX = pointX[l] + knight[i][0];
                    tempY = pointY[l] + knight[i][1];
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 0;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 0 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    }
                }

                r++;
                pointX[r] = pointX[l];
                pointY[r] = pointY[l];
                state[r] = 1;
                steps[r] = steps[l] + 1;
                moves[r] = moves[l] + 1;
                if (pointX[r] == toX && pointY[r] == toY && 1 == toState) {
                    minStep = steps[r];
                    minMove = moves[r];
                    return;
                }

                r++;
                pointX[r] = pointX[l];
                pointY[r] = pointY[l];
                state[r] = 2;
                steps[r] = steps[l] + 1;
                moves[r] = moves[l] + 1;
                if (pointX[r] == toX && pointY[r] == toY && 2 == toState) {
                    minStep = steps[r];
                    minMove = moves[r];
                    return;
                }
            } else if (state[l] == 1) {
                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempX += 1;
                    tempY += 1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 1;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 1 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempX += 1;
                    tempY += -1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 1;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 1 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempX += -1;
                    tempY += 1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 1;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 1 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempX += -1;
                    tempY += -1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 1;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 1 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                r++;
                pointX[r] = pointX[l];
                pointY[r] = pointY[l];
                state[r] = 0;
                steps[r] = steps[l] + 1;
                moves[r] = moves[l] + 1;
                if (pointX[r] == toX && pointY[r] == toY && 0 == toState) {
                    minStep = steps[r];
                    minMove = moves[r];
                    return;
                }

                r++;
                pointX[r] = pointX[l];
                pointY[r] = pointY[l];
                state[r] = 2;
                steps[r] = steps[l] + 1;
                moves[r] = moves[l] + 1;
                if (pointX[r] == toX && pointY[r] == toY && 2 == toState) {
                    minStep = steps[r];
                    minMove = moves[r];
                    return;
                }

            } else {
                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempX += 1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 2;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempX += -1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 2;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 2 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempY += 1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 2;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 2 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                tempX = pointX[l];
                tempY = pointY[l];
                for (; true;) {
                    tempY += -1;
                    if (isInMatrix(tempX, tempY)) {
                        r++;
                        pointX[r] = tempX;
                        pointY[r] = tempY;
                        state[r] = 2;
                        steps[r] = steps[l] + 1;
                        moves[r] = moves[l];
                        if (tempX == toX && tempY == toY && 2 == toState) {
                            minStep = steps[r];
                            minMove = moves[r];
                            return;
                        }
                    } else
                        break;
                }

                r++;
                pointX[r] = pointX[l];
                pointY[r] = pointY[l];
                state[r] = 0;
                steps[r] = steps[l] + 1;
                moves[r] = moves[l] + 1;
                if (pointX[r] == toX && pointY[r] == toY && 0 == toState) {
                    minStep = steps[r];
                    minMove = moves[r];
                    return;
                }

                r++;
                pointX[r] = pointX[l];
                pointY[r] = pointY[l];
                state[r] = 1;
                steps[r] = steps[l] + 1;
                moves[r] = moves[l] + 1;
                if (pointX[r] == toX && pointY[r] == toY && 1 == toState) {
                    minStep = steps[r];
                    minMove = moves[r];
                    return;
                }
            }
            l++;
        }
    }

    public static boolean isInMatrix(int x, int y) {
        if (x >= 1 && x <= n && y >= 1 && y <= n)
            return true;
        return false;
    }

}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class D {
    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        String nextLine() {
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
    }
    public static FastReader scn = new FastReader();

    public static void main(String[] args) {
        int n = scn.nextInt();
        int[] ii = new int[n*n+1] ,jj = new int[n*n+1];
        for(int i = 1; i<=n; i++){
            for(int j = 1; j<= n; j++){
                int a = scn.nextInt();
                ii[a] = i;
                jj[a] = j;
            }
        }
        int[][] mat = new int[n*n][3];
        for(int i = 1; i< n*n; i++){
            mat[i][0] = knight(n,ii[i],jj[i],ii[i+1],jj[i+1]);
            mat[i][1] = bishop(ii[i],jj[i],ii[i+1],jj[i+1]);
            mat[i][2] = rook(ii[i],jj[i],ii[i+1],jj[i+1]);
        }

        heapmover res = helper(mat,0,0);
        System.out.println(res.value + " " + res.rep);
    }

    private static class heapmover implements Comparable<heapmover>{
        int value;
        int rep;

        @Override
        public int compareTo(heapmover o) {
            if(this.value==o.value) return this.rep - o.rep;
            return this.value - o.value;
        }
    }

    private static heapmover helper(int[][] mat, int i, int j){
        if(i==mat.length) return new heapmover();

        if(mat[i][j]==-1){
            heapmover hm = new heapmover();
            hm.value = 10000000;
            return hm;
        }
        heapmover a = helper(mat,i+1,j);
        heapmover b = helper(mat,i+1,(j+1)%3);
        heapmover c = helper(mat,i+1,(j+2)%3);

        a.value = a.value + mat[i][j];
        b.value = b.value + mat[i][j] + 1;
        b.rep = b.rep + 1;
        c.value = c.value + mat[i][j] + 1;
        c.rep = c.rep + 1;

        PriorityQueue<heapmover> heap = new PriorityQueue<>();
        heap.add(a);
        heap.add(b);
        heap.add(c);

        return heap.poll();
    }

    private static int rook(int is, int js, int id, int jd){
        if(is==id || jd==js) return 1;
        return 2;
    }

    private static int bishop(int is, int js, int id, int jd){
        if(Math.abs(is-id) == Math.abs(js-jd)) return 1;
        if((Math.abs(is-id)+Math.abs(js-jd))%2==0) return 2;
        return -1;
    }

    private static class cell{
        int x, y , distance;
    }

    private static int knight(int n , int is, int js, int id, int jd){
        return bfs(n,n,is,js,id,jd);
    }

    private static int bfs(int n ,int m , int r, int c, int d1, int d2){
        int[] a = {-2,-2,-1,-1,1,1,2,2};
        int[] b = {-1,1,-2,2,-2,2,-1,1};

        boolean[][] visited = new boolean[n+1][m+1];

        LinkedList<cell> queue = new LinkedList<>();
        cell start = new cell();
        start.distance = 0;
        start.x = r;
        start.y = c;
        queue.addLast(start);

        while (!queue.isEmpty()){
            cell t = queue.removeFirst();
            visited[t.x][t.y] = true;

            if(t.x==d1 && t.y==d2) return t.distance;

            for(int i = 0 ; i< 8 ; i++){
                int x = t.x + a[i];
                int y = t.y + b[i];

                if(check(n,m,x,y) && !visited[x][y]){
                    cell aa = new cell();
                    aa.x = x;
                    aa.y = y;
                    aa.distance = t.distance+1;
                    queue.addLast(aa);
                }
            }

        }

        return -1;
    }

    private static boolean check(int n , int m , int r , int c){
        if(r>n || r <=0 || c>m || c<=0) return false;
        return true;
    }
}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class D {
    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        String nextLine() {
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
    }
    public static FastReader scn = new FastReader();

    public static void main(String[] args) {
        int n = scn.nextInt();
        int[] ii = new int[n*n+1] ,jj = new int[n*n+1];
        for(int i = 1; i<=n; i++){
            for(int j = 1; j<= n; j++){
                int a = scn.nextInt();
                ii[a] = i;
                jj[a] = j;
            }
        }
        int[][] mat = new int[n*n][3];
        for(int i = 1; i< n*n; i++){
            mat[i][0] = knight(n,ii[i],jj[i],ii[i+1],jj[i+1]);
            mat[i][1] = bishop(ii[i],jj[i],ii[i+1],jj[i+1]);
            mat[i][2] = rook(ii[i],jj[i],ii[i+1],jj[i+1]);
        }

//        System.out.println("YES");
        heapmover a = helper(mat,1,0, new heapmover[n*n+1][4]);
        heapmover b = helper(mat,1,1, new heapmover[n*n+1][4]);
        heapmover c = helper(mat,1,2, new heapmover[n*n+1][4]);

        PriorityQueue<heapmover> heap = new PriorityQueue<>();
        heap.add(a);
        heap.add(b);
        heap.add(c);

        heapmover res = heap.poll();
        System.out.println(res.value + " " + res.rep);
    }

    private static class heapmover implements Comparable<heapmover>{
        int value;
        int rep;

        @Override
        public int compareTo(heapmover o) {
            if(this.value==o.value) return this.rep - o.rep;
            return this.value - o.value;
        }
    }

    private static heapmover helper(int[][] mat, int i, int j, heapmover[][] strg){
        if(mat[i][j]==-1){
            heapmover hm = new heapmover();
            hm.value = 10000000;
            return strg[i][j] = hm;
        }
        if(i==mat.length-1) {
            heapmover hm = new heapmover();
            hm.value = mat[i][j];
            hm.rep = 0;
            return hm;
        }

        if(strg[i][j]!=null ) return strg[i][j];

        heapmover a = helper(mat,i+1,j,strg);
        heapmover b = helper(mat,i+1,(j+1)%3, strg);
        heapmover c = helper(mat,i+1,(j+2)%3, strg);

        heapmover aa = new heapmover();
        heapmover bb = new heapmover();
        heapmover cc = new heapmover();

        aa.value = a.value + mat[i][j];
        aa.rep = a.rep;
        bb.value = b.value + mat[i][j] + 1;
        bb.rep = b.rep + 1;
        cc.value = c.value + mat[i][j] + 1;
        cc.rep = c.rep + 1;

        PriorityQueue<heapmover> heap = new PriorityQueue<>();
        heap.add(aa);
        heap.add(bb);
        heap.add(cc);

        heapmover d = heap.poll();
        strg[i][j] = d;
        return d;
    }

    private static int rook(int is, int js, int id, int jd){
        if(is==id || jd==js) return 1;
        return 2;
    }

    private static int bishop(int is, int js, int id, int jd){
        if(Math.abs(is-id) == Math.abs(js-jd)) return 1;
        if((Math.abs(is-id)+Math.abs(js-jd))%2==0) return 2;
        return -1;
    }

    private static class cell{
        int x, y , distance;
    }

    private static int knight(int n , int is, int js, int id, int jd){
        return bfs(n,n,is,js,id,jd);
    }

    private static int bfs(int n ,int m , int r, int c, int d1, int d2){
        int[] a = {-2,-2,-1,-1,1,1,2,2};
        int[] b = {-1,1,-2,2,-2,2,-1,1};

        boolean[][] visited = new boolean[n+1][m+1];

        LinkedList<cell> queue = new LinkedList<>();
        cell start = new cell();
        start.distance = 0;
        start.x = r;
        start.y = c;
        queue.addLast(start);

        while (!queue.isEmpty()){
            cell t = queue.removeFirst();
            visited[t.x][t.y] = true;

            if(t.x==d1 && t.y==d2) return t.distance;

            for(int i = 0 ; i< 8 ; i++){
                int x = t.x + a[i];
                int y = t.y + b[i];

                if(check(n,m,x,y) && !visited[x][y]){
                    cell aa = new cell();
                    aa.x = x;
                    aa.y = y;
                    aa.distance = t.distance+1;
                    queue.addLast(aa);
                }
            }

        }

        return -1;
    }

    private static boolean check(int n , int m , int r , int c){
        if(r>n || r <=0 || c>m || c<=0) return false;
        return true;
    }
}

//package baobab;

import java.io.*;
import java.util.*;

public class D {

    public static void main(String[] args) {
        Solver solver = new Solver();
    }

    static class Solver {
        IO io;

        public Solver() {
            this.io = new IO();
            try {
                solve();
            } catch (RuntimeException e) {
                if (!e.getMessage().equals("Clean exit")) {
                    throw e;
                }
            } finally {
                io.close();
            }
        }

        /****************************** START READING HERE ********************************/

        void solve() {
            int n = io.nextInt();
            int[][] b = new int[n][n];
            for (int y=0; y<n; y++) {
                for (int x=0; x<n; x++) {
                    b[y][x] = io.nextInt();
                }
            }
            int end = n*n;
            int[] x = new int[end+1];
            int[] y = new int[end+1];
            for (int i=0; i<n; i++) {
                for (int j=0; j<n; j++) {
                    int v = b[i][j];
                    y[v] = i;
                    x[v] = j;
                }
            }

            State ans = null;
            State[][][][][] leastSteps = new State[n][n][n][n][3];
            Deque<State> q = new ArrayDeque<>();
            q.add(new State(y[1], x[1], y[2], x[2], ROOK, 0, 0));
            q.add(new State(y[1], x[1], y[2], x[2], KNIGHT, 0, 0));
            q.add(new State(y[1], x[1], y[2], x[2], BISHOP, 0, 0));
            while (!q.isEmpty()) {
                State s = q.pollFirst();
                State prev = leastSteps[s.currY][s.currX][s.goalY][s.goalX][s.piece];
                if (prev != null) {
                    if (prev.steps < s.steps) continue;
                    if (prev.steps == s.steps && prev.replacements <= s.replacements) continue;
                }
                leastSteps[s.currY][s.currX][s.goalY][s.goalX][s.piece] = s;

                // intermediary goal reached?
                if (s.currX == s.goalX && s.currY == s.goalY) {

                    if (b[s.currY][s.currX] == end) {
                        // actually reached end state with fewest steps but not necessarily fewest replacements for that step size
                        if (ans == null) ans = s;
                        else if (ans.replacements >= s.replacements) ans = s;
                        continue; // stop bfs but dont break because we may find new solution with same steps but fewer replacements
                    }

                    // change state to set next goal.
                    int prevGoal = b[s.goalY][s.goalX];
                    int newGoal = prevGoal + 1;
                    int newGoalX = x[newGoal];
                    int newGoalY = y[newGoal];
                    // front of the queue!
                    q.addFirst(new State(s.goalY, s.goalX, newGoalY, newGoalX, s.piece, s.steps, s.replacements));
                    continue;
                }

                // change piece?
                q.addLast(new State(s.currY, s.currX, s.goalY, s.goalX, ROOK, s.steps+1, s.replacements+1));
                q.addLast(new State(s.currY, s.currX, s.goalY, s.goalX, BISHOP, s.steps+1, s.replacements+1));
                q.addLast(new State(s.currY, s.currX, s.goalY, s.goalX, KNIGHT, s.steps+1, s.replacements+1));

                // move current piece?
                if (s.piece == ROOK) {
                    for (int newY=0; newY<n; newY++) q.addLast(new State(newY, s.currX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    for (int newX=0; newX<n; newX++) q.addLast(new State(s.currY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                } else if (s.piece == KNIGHT) {
                    int newY = s.currY + 1;
                    int newX = s.currX + 2;
                    if (newY >= 0 && newY < n && newX >= 0 && newX < n) {
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY + 1;
                    newX = s.currX - 2;
                    if (newY >= 0 && newY < n && newX >= 0 && newX < n) {
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY - 1;
                    newX = s.currX - 2;
                    if (newY >= 0 && newY < n && newX >= 0 && newX < n) {
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY - 1;
                    newX = s.currX + 2;
                    if (newY >= 0 && newY < n && newX >= 0 && newX < n) {
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY + 2;
                    newX = s.currX + 1;
                    if (newY >= 0 && newY < n && newX >= 0 && newX < n) {
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY - 2;
                    newX = s.currX + 1;
                    if (newY >= 0 && newY < n && newX >= 0 && newX < n) {
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY + 2;
                    newX = s.currX - 1;
                    if (newY >= 0 && newY < n && newX >= 0 && newX < n) {
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY - 2;
                    newX = s.currX - 1;
                    if (newY >= 0 && newY < n && newX >= 0 && newX < n) {
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }

                } else if (s.piece == BISHOP) {
                    int newY = s.currY;
                    int newX = s.currX;
                    int diff = 1;
                    while (true) {
                        newY += diff;
                        newX += diff;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY;
                    newX = s.currX;
                    diff = 1;
                    while (true) {
                        newY += diff;
                        newX -= diff;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY;
                    newX = s.currX;
                    diff = 1;
                    while (true) {
                        newY -= diff;
                        newX -= diff;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY;
                    newX = s.currX;
                    diff = 1;
                    while (true) {
                        newY -= diff;
                        newX += diff;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                }
            }

            io.println(ans.steps + " " + ans.replacements);
        }

        static int ROOK = 0;
        static int KNIGHT = 1;
        static int BISHOP = 2;

        class State {
            int currY;
            int currX;
            int goalY;
            int goalX;
            int piece;
            int steps;
            int replacements;

            public State(int currY, int currX, int goalY, int goalX, int piece, int steps, int replacements) {
                this.currY = currY;
                this.currX = currX;
                this.goalY = goalY;
                this.goalX = goalX;
                this.piece = piece;
                this.steps = steps;
                this.replacements = replacements;
            }

            @Override
            public boolean equals(Object o) {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;
                State state = (State) o;
                return currY == state.currY &&
                        currX == state.currX &&
                        goalY == state.goalY &&
                        goalX == state.goalX &&
                        piece == state.piece;
            }

            @Override
            public int hashCode() {
                return Objects.hash(currY, currX, goalY, goalX, piece);
            }
        }

        /************************** UTILITY CODE BELOW THIS LINE **************************/

        long MOD = (long)1e9 + 7;

        boolean closeToZero(double v) {
            // Check if double is close to zero, considering precision issues.
            return Math.abs(v) <= 0.0000000001;
        }

        class DrawGrid {

            void draw(boolean[][] d) {
                System.out.print("  ");
                for (int x=0; x<d[0].length; x++) {
                    System.out.print(" " + x + " ");
                }
                System.out.println("");
                for (int y=0; y<d.length; y++) {
                    System.out.print(y + " ");
                    for (int x=0; x<d[0].length; x++) {
                        System.out.print((d[y][x] ? "[x]" : "[ ]"));
                    }
                    System.out.println("");
                }
            }

            void draw(int[][] d) {
                int max = 1;
                for (int y=0; y<d.length; y++) {
                    for (int x=0; x<d[0].length; x++) {
                        max = Math.max(max, ("" + d[y][x]).length());
                    }
                }
                System.out.print("  ");
                String format = "%" + (max+2) + "s";
                for (int x=0; x<d[0].length; x++) {
                    System.out.print(String.format(format, x) + " ");
                }
                format = "%" + (max) + "s";
                System.out.println("");
                for (int y=0; y<d.length; y++) {
                    System.out.print(y + " ");
                    for (int x=0; x<d[0].length; x++) {
                        System.out.print(" [" + String.format(format, (d[y][x])) + "]");
                    }
                    System.out.println("");
                }
            }

        }

        class IDval implements Comparable<IDval> {
            int steps;
            long replacements;

            public IDval(int steps, long replacements) {
                this.replacements = replacements;
                this.steps = steps;
            }

            @Override
            public int compareTo(IDval o) {
                if (this.replacements < o.replacements) return -1;
                if (this.replacements > o.replacements) return 1;
                return this.steps - o.steps;
            }
        }

        private class ElementCounter {
            private HashMap<Long, Integer> elements;

            public ElementCounter() {
                elements = new HashMap<>();
            }

            public void add(long element) {
                int count = 1;
                Integer prev = elements.get(element);
                if (prev != null) count += prev;
                elements.put(element, count);
            }

            public void remove(long element) {
                int count = elements.remove(element);
                count--;
                if (count > 0) elements.put(element, count);
            }

            public int get(long element) {
                Integer val = elements.get(element);
                if (val == null) return 0;
                return val;
            }

            public int size() {
                return elements.size();
            }
        }

        class StringCounter {
            HashMap<String, Integer> elements;

            public StringCounter() {
                elements = new HashMap<>();
            }

            public void add(String identifier) {
                int count = 1;
                Integer prev = elements.get(identifier);
                if (prev != null) count += prev;
                elements.put(identifier, count);
            }

            public void remove(String identifier) {
                int count = elements.remove(identifier);
                count--;
                if (count > 0) elements.put(identifier, count);
            }

            public long get(String identifier) {
                Integer val = elements.get(identifier);
                if (val == null) return 0;
                return val;
            }

            public int size() {
                return elements.size();
            }
        }

        class DisjointSet {
            /** Union Find / Disjoint Set data structure. */
            int[] size;
            int[] parent;
            int componentCount;

            public DisjointSet(int n) {
                componentCount = n;
                size = new int[n];
                parent = new int[n];
                for (int i=0; i<n; i++) parent[i] = i;
                for (int i=0; i<n; i++) size[i] = 1;
            }

            public void join(int a, int b) {
                /* Find roots */
                int rootA = parent[a];
                int rootB = parent[b];
                while (rootA != parent[rootA]) rootA = parent[rootA];
                while (rootB != parent[rootB]) rootB = parent[rootB];

                if (rootA == rootB) {
                    /* Already in the same set */
                    return;
                }

                /* Merge smaller set into larger set. */
                if (size[rootA] > size[rootB]) {
                    size[rootA] += size[rootB];
                    parent[rootB] = rootA;
                } else {
                    size[rootB] += size[rootA];
                    parent[rootA] = rootB;
                }
                componentCount--;
            }

        }

        class Trie {
            int N;
            int Z;
            int nextFreeId;
            int[][] pointers;
            boolean[] end;

            /** maxLenSum = maximum possible sum of length of words */
            public Trie(int maxLenSum, int alphabetSize) {
                this.N = maxLenSum;
                this.Z = alphabetSize;
                this.nextFreeId = 1;
                pointers = new int[N+1][alphabetSize];
                end = new boolean[N+1];
            }

            public void addWord(String word) {
                int curr = 0;
                for (int j=0; j<word.length(); j++) {
                    int c = word.charAt(j) - 'a';
                    int next = pointers[curr][c];
                    if (next == 0) {
                        next = nextFreeId++;
                        pointers[curr][c] = next;
                    }
                    curr = next;
                }
                end[curr] = true;
            }

            public boolean hasWord(String word) {
                int curr = 0;
                for (int j=0; j<word.length(); j++) {
                    int c = word.charAt(j) - 'a';
                    int next = pointers[curr][c];
                    if (next == 0) return false;
                    curr = next;
                }
                return end[curr];
            }

        }

        private static class Prob {

            /** For heavy calculations on probabilities, this class
             *  provides more accuracy & efficiency than doubles.
             *  Math explained: https://en.wikipedia.org/wiki/Log_probability
             *  Quick start:
             *      - Instantiate probabilities, eg. Prob a = new Prob(0.75)
             *      - add(), multiply() return new objects, can perform on nulls & NaNs.
             *      - get() returns probability as a readable double */

            /** Logarithmized probability. Note: 0% represented by logP NaN. */
            private double logP;

            /** Construct instance with real probability. */
            public Prob(double real) {
                if (real > 0) this.logP = Math.log(real);
                else this.logP = Double.NaN;
            }

            /** Construct instance with already logarithmized value. */
            static boolean dontLogAgain = true;
            public Prob(double logP, boolean anyBooleanHereToChooseThisConstructor) {
                this.logP = logP;
            }

            /** Returns real probability as a double. */
            public double get() {
                return Math.exp(logP);
            }

            @Override
            public String toString() {
                return ""+get();
            }

            /***************** STATIC METHODS BELOW ********************/

            /** Note: returns NaN only when a && b are both NaN/null. */
            public static Prob add(Prob a, Prob b) {
                if (nullOrNaN(a) && nullOrNaN(b)) return new Prob(Double.NaN, dontLogAgain);
                if (nullOrNaN(a)) return copy(b);
                if (nullOrNaN(b)) return copy(a);

                double x = Math.max(a.logP, b.logP);
                double y = Math.min(a.logP, b.logP);
                double sum = x + Math.log(1 + Math.exp(y - x));
                return new Prob(sum, dontLogAgain);
            }

            /** Note: multiplying by null or NaN produces NaN (repping 0% real prob). */
            public static Prob multiply(Prob a, Prob b) {
                if (nullOrNaN(a) || nullOrNaN(b)) return new Prob(Double.NaN, dontLogAgain);
                return new Prob(a.logP + b.logP, dontLogAgain);
            }

            /** Returns true if p is null or NaN. */
            private static boolean nullOrNaN(Prob p) {
                return (p == null || Double.isNaN(p.logP));
            }

            /** Returns a new instance with the same value as original. */
            private static Prob copy(Prob original) {
                return new Prob(original.logP, dontLogAgain);
            }
        }

        class Binary implements Comparable<Binary> {

            /**
             * Use example: Binary b = new Binary(Long.toBinaryString(53249834L));
             *
             * When manipulating small binary strings, instantiate new Binary(string)
             * When just reading large binary strings, instantiate new Binary(string,true)
             * get(int i) returns a character '1' or '0', not an int.
             */

            private boolean[] d;
            private int first; // Starting from left, the first (most remarkable) '1'
            public int length;

            public Binary(String binaryString) {
                this(binaryString, false);
            }
            public Binary(String binaryString, boolean initWithMinArraySize) {
                length = binaryString.length();
                int size = Math.max(2*length, 1);
                first = length/4;
                if (initWithMinArraySize) {
                    first = 0;
                    size = Math.max(length, 1);
                }
                d = new boolean[size];
                for (int i=0; i<length; i++) {
                    if (binaryString.charAt(i) == '1') d[i+first] = true;
                }
            }

            public void addFirst(char c) {
                if (first-1 < 0) doubleArraySize();
                first--;
                d[first] = (c == '1' ? true : false);
                length++;
            }

            public void addLast(char c) {
                if (first+length >= d.length) doubleArraySize();
                d[first+length] = (c == '1' ? true : false);
                length++;
            }

            private void doubleArraySize() {
                boolean[] bigArray = new boolean[(d.length+1) * 2];
                int newFirst = bigArray.length / 4;
                for (int i=0; i<length; i++) {
                    bigArray[i + newFirst] = d[i + first];
                }
                first = newFirst;
                d = bigArray;
            }

            public boolean flip(int i) {
                boolean value = (this.d[first+i] ? false : true);
                this.d[first+i] = value;
                return value;
            }

            public void set(int i, char c) {
                boolean value = (c == '1' ? true : false);
                this.d[first+i] = value;
            }

            public char get(int i) {
                return (this.d[first+i] ? '1' : '0');
            }

            @Override
            public int compareTo(Binary o) {
                if (this.length != o.length) return this.length - o.length;
                int len = this.length;
                for (int i=0; i<len; i++) {
                    int diff = this.get(i) - o.get(i);
                    if (diff != 0) return diff;
                }
                return 0;
            }

            @Override
            public String toString() {
                StringBuilder sb = new StringBuilder();
                for (int i=0; i<length; i++) {
                    sb.append(d[i+first] ? '1' : '0');
                }
                return sb.toString();
            }

        }

        /************************** Range queries **************************/

        class FenwickMin {
            long n;
            long[] original;
            long[] bottomUp;
            long[] topDown;

            public FenwickMin(int n) {
                this.n = n;
                original = new long[n+2];
                bottomUp = new long[n+2];
                topDown = new long[n+2];
            }

            public void set(int modifiedNode, long value) {
                long replaced = original[modifiedNode];
                original[modifiedNode] = value;
                // Update left tree
                int i = modifiedNode;
                long v = value;
                while (i <= n) {
                    if (v > bottomUp[i]) {
                        if (replaced == bottomUp[i]) {
                            v = Math.min(v, original[i]);
                            for (int r=1 ;; r++) {
                                int x = (i&-i)>>>r;
                                if (x == 0) break;
                                int child = i-x;
                                v = Math.min(v, bottomUp[child]);
                            }
                        } else break;
                    }
                    if (v == bottomUp[i]) break;
                    bottomUp[i] = v;
                    i += (i&-i);
                }
                // Update right tree
                i = modifiedNode;
                v = value;
                while (i > 0) {
                    if (v > topDown[i]) {
                        if (replaced == topDown[i]) {
                            v = Math.min(v, original[i]);
                            for (int r=1 ;; r++) {
                                int x = (i&-i)>>>r;
                                if (x == 0) break;
                                int child = i+x;
                                if (child > n+1) break;
                                v = Math.min(v, topDown[child]);
                            }
                        } else break;
                    }
                    if (v == topDown[i]) break;
                    topDown[i] = v;
                    i -= (i&-i);
                }
            }

            public long getMin(int a, int b) {
                long min = original[a];
                int prev = a;
                int curr = prev + (prev&-prev); // parent right hand side
                while (curr <= b) {
                    min = Math.min(min, topDown[prev]); // value from the other tree
                    prev = curr;
                    curr = prev + (prev&-prev);;
                }
                min = Math.min(min, original[prev]);
                prev = b;
                curr = prev - (prev&-prev); // parent left hand side
                while (curr >= a) {
                    min = Math.min(min,bottomUp[prev]); // value from the other tree
                    prev = curr;
                    curr = prev - (prev&-prev);
                }
                return min;
            }

        }

        class FenwickSum {
            public long[] d;

            public FenwickSum(int n) {
                d=new long[n+1];
            }

            /** a[0] must be unused. */
            public FenwickSum(long[] a) {
                d=new long[a.length];
                for (int i=1; i<a.length; i++) {
                    modify(i, a[i]);
                }
            }

            /** Do not modify i=0. */
            void modify(int i, long v) {
                while (i<d.length) {
                    d[i] += v;
                    // Move to next uplink on the RIGHT side of i
                    i += (i&-i);
                }
            }

            /** Returns sum from a to b, *BOTH* inclusive. */
            long getSum(int a, int b) {
                return getSum(b) - getSum(a-1);
            }

            private long getSum(int i) {
                long sum = 0;
                while (i>0) {
                    sum += d[i];
                    // Move to next uplink on the LEFT side of i
                    i -= (i&-i);
                }
                return sum;
            }
        }

        class SegmentTree {

            /* Provides log(n) operations for:
             * - Range query (sum, min or max)
             * - Range update ("+8 to all values between indexes 4 and 94")
             */

            int N;
            long[] lazy;
            long[] sum;
            long[] min;
            long[] max;
            boolean supportSum;
            boolean supportMin;
            boolean supportMax;

            public SegmentTree(int n) {
                this(n, true, true, true);
            }

            public SegmentTree(int n, boolean supportSum, boolean supportMin, boolean supportMax) {
                for (N=2; N<n;) N*=2;
                this.lazy = new long[2*N];
                this.supportSum = supportSum;
                this.supportMin = supportMin;
                this.supportMax = supportMax;
                if (this.supportSum) this.sum = new long[2*N];
                if (this.supportMin) this.min = new long[2*N];
                if (this.supportMax) this.max = new long[2*N];
            }

            void modifyRange(long x, int a, int b) {
                modifyRec(a, b, 1, 0, N-1, x);
            }

            void setRange() {
                //TODO
            }

            long getSum(int a, int b) {
                return querySum(a, b, 1, 0, N-1);
            }

            long getMin(int a, int b) {
                return queryMin(a, b, 1, 0, N-1);
            }

            long getMax(int a, int b) {
                return queryMax(a, b, 1, 0, N-1);
            }

            private long querySum(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return 0;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    int count = wantedRight - wantedLeft + 1;
                    return sum[i] + count * lazy[i];
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                long left = querySum(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1);
                long right = querySum(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight);
                return left + right;
            }

            private long queryMin(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return 0;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    return min[i] + lazy[i];
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                long left = queryMin(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1);
                long right = queryMin(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight);
                return min(left, right);
            }

            private long queryMax(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return 0;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    return max[i] + lazy[i];
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                long left = queryMax(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1);
                long right = queryMax(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight);
                return max(left, right);
            }

            private void modifyRec(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight, long value) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    lazy[i] += value;
                    return;
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                modifyRec(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1, value);
                modifyRec(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight, value);
                if (supportSum) sum[i] += value * (min(actualRight, wantedRight) - max(actualLeft, wantedLeft) + 1);
                if (supportMin) min[i] = min(min[2*i] + lazy[2*i], min[2*i+1] + lazy[2*i+1]);
                if (supportMax) max[i] = max(max[2*i] + lazy[2*i], max[2*i+1] + lazy[2*i+1]);
            }

            private void propagate(int i, int actualLeft, int actualRight) {
                lazy[2*i] += lazy[i];
                lazy[2*i+1] += lazy[i];
                if (supportSum) sum[i] += lazy[i] * (actualRight - actualLeft + 1);
                if (supportMin) min[i] += lazy[i];
                if (supportMax) max[i] += lazy[i];
                lazy[i] = 0;
            }

        }

        /***************************** Graphs *****************************/

        List<Integer>[] toGraph(IO io, int n) {
            List<Integer>[] g = new ArrayList[n+1];
            for (int i=1; i<=n; i++) g[i] = new ArrayList<>();
            for (int i=1; i<=n-1; i++) {
                int a = io.nextInt();
                int b = io.nextInt();
                g[a].add(b);
                g[b].add(a);
            }
            return g;
        }

        class Graph {
            HashMap<Long, List<Long>> edges;

            public Graph() {
                edges = new HashMap<>();
            }

            List<Long> getSetNeighbors(Long node) {
                List<Long> neighbors = edges.get(node);
                if (neighbors == null) {
                    neighbors = new ArrayList<>();
                    edges.put(node, neighbors);
                }
                return neighbors;
            }

            void addBiEdge(Long a, Long b) {
                addEdge(a, b);
                addEdge(b, a);
            }

            void addEdge(Long from, Long to) {
                getSetNeighbors(to); // make sure all have initialized lists
                List<Long> neighbors = getSetNeighbors(from);
                neighbors.add(to);
            }

            // topoSort variables
            int UNTOUCHED = 0;
            int FINISHED = 2;
            int INPROGRESS = 1;
            HashMap<Long, Integer> vis;
            List<Long> topoAns;
            List<Long> failDueToCycle = new ArrayList<Long>() {{ add(-1L); }};

            List<Long> topoSort() {
                topoAns = new ArrayList<>();
                vis = new HashMap<>();
                for (Long a : edges.keySet()) {
                    if (!topoDFS(a)) return failDueToCycle;
                }
                Collections.reverse(topoAns);
                return topoAns;
            }

            boolean topoDFS(long curr) {
                Integer status = vis.get(curr);
                if (status == null) status = UNTOUCHED;
                if (status == FINISHED) return true;
                if (status == INPROGRESS) {
                    return false;
                }
                vis.put(curr, INPROGRESS);
                for (long next : edges.get(curr)) {
                    if (!topoDFS(next)) return false;
                }
                vis.put(curr, FINISHED);
                topoAns.add(curr);
                return true;
            }

        }

        public class StronglyConnectedComponents {

            /** Kosaraju's algorithm */

            ArrayList<Integer>[] forw;
            ArrayList<Integer>[] bacw;

            /** Use: getCount(2, new int[] {1,2}, new int[] {2,1}) */
            public int getCount(int n, int[] mista, int[] minne) {
                forw = new ArrayList[n+1];
                bacw = new ArrayList[n+1];
                for (int i=1; i<=n; i++) {
                    forw[i] = new ArrayList<Integer>();
                    bacw[i] = new ArrayList<Integer>();
                }
                for (int i=0; i<mista.length; i++) {
                    int a = mista[i];
                    int b = minne[i];
                    forw[a].add(b);
                    bacw[b].add(a);
                }
                int count = 0;
                List<Integer> list = new ArrayList<Integer>();
                boolean[] visited = new boolean[n+1];
                for (int i=1; i<=n; i++) {
                    dfsForward(i, visited, list);
                }
                visited = new boolean[n+1];
                for (int i=n-1; i>=0; i--) {
                    int node = list.get(i);
                    if (visited[node]) continue;
                    count++;
                    dfsBackward(node, visited);
                }
                return count;
            }

            public void dfsForward(int i, boolean[] visited, List<Integer> list) {
                if (visited[i]) return;
                visited[i] = true;
                for (int neighbor : forw[i]) {
                    dfsForward(neighbor, visited, list);
                }
                list.add(i);
            }

            public void dfsBackward(int i, boolean[] visited) {
                if (visited[i]) return;
                visited[i] = true;
                for (int neighbor : bacw[i]) {
                    dfsBackward(neighbor, visited);
                }
            }
        }

        class LCAFinder {

            /* O(n log n) Initialize: new LCAFinder(graph)
             * O(log n) Queries: find(a,b) returns lowest common ancestor for nodes a and b */

            int[] nodes;
            int[] depths;
            int[] entries;
            int pointer;
            FenwickMin fenwick;

            public LCAFinder(List<Integer>[] graph) {
                this.nodes = new int[(int)10e6];
                this.depths = new int[(int)10e6];
                this.entries = new int[graph.length];
                this.pointer = 1;
                boolean[] visited = new boolean[graph.length+1];
                dfs(1, 0, graph, visited);
                fenwick = new FenwickMin(pointer-1);
                for (int i=1; i<pointer; i++) {
                    fenwick.set(i, depths[i] * 1000000L + i);
                }
            }

            private void dfs(int node, int depth, List<Integer>[] graph, boolean[] visited) {
                visited[node] = true;
                entries[node] = pointer;
                nodes[pointer] = node;
                depths[pointer] = depth;
                pointer++;
                for (int neighbor : graph[node]) {
                    if (visited[neighbor]) continue;
                    dfs(neighbor, depth+1, graph, visited);
                    nodes[pointer] = node;
                    depths[pointer] = depth;
                    pointer++;
                }
            }

            public int find(int a, int b) {
                int left = entries[a];
                int right = entries[b];
                if (left > right) {
                    int temp = left;
                    left = right;
                    right = temp;
                }
                long mixedBag = fenwick.getMin(left, right);
                int index = (int) (mixedBag % 1000000L);
                return nodes[index];
            }
        }

        /**************************** Geometry ****************************/

        class Point {
            int y;
            int x;

            public Point(int y, int x) {
                this.y = y;
                this.x = x;
            }
        }

        boolean segmentsIntersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
            // Returns true if segment 1-2 intersects segment 3-4

            if (x1 == x2 && x3 == x4) {
                // Both segments are vertical
                if (x1 != x3) return false;
                if (min(y1,y2) < min(y3,y4)) {
                    return max(y1,y2) >= min(y3,y4);
                } else {
                    return max(y3,y4) >= min(y1,y2);
                }
            }
            if (x1 == x2) {
                // Only segment 1-2 is vertical. Does segment 3-4 cross it? y = a*x + b
                double a34 = (y4-y3)/(x4-x3);
                double b34 = y3 - a34*x3;
                double y = a34 * x1 + b34;
                return y >= min(y1,y2) && y <= max(y1,y2) && x1 >= min(x3,x4) && x1 <= max(x3,x4);
            }
            if (x3 == x4) {
                // Only segment 3-4 is vertical. Does segment 1-2 cross it? y = a*x + b
                double a12 = (y2-y1)/(x2-x1);
                double b12 = y1 - a12*x1;
                double y = a12 * x3 + b12;
                return y >= min(y3,y4) && y <= max(y3,y4) && x3 >= min(x1,x2) && x3 <= max(x1,x2);
            }
            double a12 = (y2-y1)/(x2-x1);
            double b12 = y1 - a12*x1;
            double a34 = (y4-y3)/(x4-x3);
            double b34 = y3 - a34*x3;
            if (closeToZero(a12 - a34)) {
                // Parallel lines
                return closeToZero(b12 - b34);
            }
            // Non parallel non vertical lines intersect at x. Is x part of both segments?
            double x = -(b12-b34)/(a12-a34);
            return x >= min(x1,x2) && x <= max(x1,x2) && x >= min(x3,x4) && x <= max(x3,x4);
        }

        boolean pointInsideRectangle(Point p, List<Point> r, boolean countBorderAsInside) {
            Point a = r.get(0);
            Point b = r.get(1);
            Point c = r.get(2);
            Point d = r.get(3);
            double apd = areaOfTriangle(a, p, d);
            double dpc = areaOfTriangle(d, p, c);
            double cpb = areaOfTriangle(c, p, b);
            double pba = areaOfTriangle(p, b, a);
            double sumOfAreas = apd + dpc + cpb + pba;
            if (closeToZero(sumOfAreas - areaOfRectangle(r))) {
                if (closeToZero(apd) || closeToZero(dpc) || closeToZero(cpb) || closeToZero(pba)) {
                    return countBorderAsInside;
                }
                return true;
            }
            return false;
        }

        double areaOfTriangle(Point a, Point b, Point c) {
            return 0.5 * Math.abs((a.x-c.x)*(b.y-a.y)-(a.x-b.x)*(c.y-a.y));
        }

        double areaOfRectangle(List<Point> r) {
            double side1xDiff = r.get(0).x - r.get(1).x;
            double side1yDiff = r.get(0).y - r.get(1).y;
            double side2xDiff = r.get(1).x - r.get(2).x;
            double side2yDiff = r.get(1).y - r.get(2).y;
            double side1 = Math.sqrt(side1xDiff * side1xDiff + side1yDiff * side1yDiff);
            double side2 = Math.sqrt(side2xDiff * side2xDiff + side2yDiff * side2yDiff);
            return side1 * side2;
        }

        boolean pointsOnSameLine(double x1, double y1, double x2, double y2, double x3, double y3) {
            double areaTimes2 = x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2);
            return (closeToZero(areaTimes2));
        }

        class PointToLineSegmentDistanceCalculator {

            // Just call this
            double minDistFromPointToLineSegment(double point_x, double point_y, double x1, double y1, double x2, double y2) {
                return Math.sqrt(distToSegmentSquared(point_x, point_y, x1, y1, x2, y2));
            }

            private double distToSegmentSquared(double point_x, double point_y, double x1, double y1, double x2, double y2) {
                double l2 = dist2(x1,y1,x2,y2);
                if (l2 == 0) return dist2(point_x, point_y, x1, y1);
                double t = ((point_x - x1) * (x2 - x1) + (point_y - y1) * (y2 - y1)) / l2;
                if (t < 0) return dist2(point_x, point_y, x1, y1);
                if (t > 1) return dist2(point_x, point_y, x2, y2);

                double com_x = x1 + t * (x2 - x1);
                double com_y = y1 + t * (y2 - y1);
                return dist2(point_x, point_y, com_x, com_y);
            }

            private double dist2(double x1, double y1, double x2, double y2) {
                return Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2);
            }

        }

        /****************************** Math ******************************/

        long pow(long base, int exp) {
            if (exp == 0) return 1L;
            long x = pow(base, exp/2);
            long ans = x * x;
            if (exp % 2 != 0) ans *= base;
            return ans;
        }

        long gcd(long... v) {
            /** Chained calls to Euclidean algorithm. */
            if (v.length == 1) return v[0];
            long ans = gcd(v[1], v[0]);
            for (int i=2; i<v.length; i++) {
                ans = gcd(ans, v[i]);
            }
            return ans;
        }

        long gcd(long a, long b) {
            /** Euclidean algorithm. */
            if (b == 0) return a;
            return gcd(b, a%b);
        }

        int[] generatePrimesUpTo(int last) {
            /* Sieve of Eratosthenes. Practically O(n). Values of 0 indicate primes. */
            int[] div = new int[last+1];
            for (int x=2; x<=last; x++) {
                if (div[x] > 0) continue;
                for (int u=2*x; u<=last; u+=x) {
                    div[u] = x;
                }
            }
            return div;
        }

        long lcm(long a, long b) {
            /** Least common multiple */
            return a * b / gcd(a,b);
        }

        class BaseConverter {

            /* Palauttaa luvun esityksen kannassa base */
            public String convert(Long number, int base) {
                return Long.toString(number, base);
            }

            /* Palauttaa luvun esityksen kannassa baseTo, kun annetaan luku Stringinä kannassa baseFrom */
            public String convert(String number, int baseFrom, int baseTo) {
                return Long.toString(Long.parseLong(number, baseFrom), baseTo);
            }

            /* Tulkitsee kannassa base esitetyn luvun longiksi (kannassa 10) */
            public long longify(String number, int baseFrom) {
                return Long.parseLong(number, baseFrom);
            }
        }

        class BinomialCoefficients {
            /** Total number of K sized unique combinations from pool of size N (unordered)
             N! / ( K! (N - K)! )   */

            /** For simple queries where output fits in long. */
            public long biCo(long n, long k) {
                long r = 1;
                if (k > n) return 0;
                for (long d = 1; d <= k; d++) {
                    r *= n--;
                    r /= d;
                }
                return r;
            }

            /** For multiple queries with same n, different k. */
            public long[] precalcBinomialCoefficientsK(int n, int maxK) {
                long v[] = new long[maxK+1];
                v[0] = 1; // nC0 == 1
                for (int i=1; i<=n; i++) {
                    for (int j=Math.min(i,maxK); j>0; j--) {
                        v[j] = v[j] + v[j-1]; // Pascal's triangle
                    }
                }
                return v;
            }

            /** When output needs % MOD. */
            public long[] precalcBinomialCoefficientsK(int n, int k, long M) {
                long v[] = new long[k+1];
                v[0] = 1; // nC0 == 1
                for (int i=1; i<=n; i++) {
                    for (int j=Math.min(i,k); j>0; j--) {
                        v[j] = v[j] + v[j-1]; // Pascal's triangle
                        v[j] %= M;
                    }
                }
                return v;
            }
        }

        /**************************** Strings ****************************/

        class Zalgo {

            public int pisinEsiintyma(String haku, String kohde) {
                char[] s = new char[haku.length() + 1 + kohde.length()];
                for (int i=0; i<haku.length(); i++) {
                    s[i] = haku.charAt(i);
                }
                int j = haku.length();
                s[j++] = '#';
                for (int i=0; i<kohde.length(); i++) {
                    s[j++] = kohde.charAt(i);
                }
                int[] z = toZarray(s);
                int max = 0;
                for (int i=haku.length(); i<z.length; i++) {
                    max = Math.max(max, z[i]);
                }
                return max;
            }

            public int[] toZarray(char[] s) {
                int n = s.length;
                int[] z = new int[n];
                int a = 0, b = 0;
                for (int i = 1; i < n; i++) {
                    if (i > b) {
                        for (int j = i; j < n && s[j - i] == s[j]; j++)  z[i]++;
                    }
                    else {
                        z[i] = z[i - a];
                        if (i + z[i - a] > b) {
                            for (int j = b + 1; j < n && s[j - i] == s[j]; j++) z[i]++;
                            a = i;
                            b = i + z[i] - 1;
                        }
                    }
                }
                return z;
            }

            public List<Integer> getStartIndexesWhereWordIsFound(String haku, String kohde) {
                // this is alternative use case
                char[] s = new char[haku.length() + 1 + kohde.length()];
                for (int i=0; i<haku.length(); i++) {
                    s[i] = haku.charAt(i);
                }
                int j = haku.length();
                s[j++] = '#';
                for (int i=0; i<kohde.length(); i++) {
                    s[j++] = kohde.charAt(i);
                }
                int[] z = toZarray(s);
                List<Integer> indexes = new ArrayList<>();
                for (int i=haku.length(); i<z.length; i++) {
                    if (z[i] < haku.length()) continue;
                    indexes.add(i);
                }
                return indexes;
            }

        }

        class StringHasher {

            class HashedString {
                long[] hashes;
                long[] modifiers;

                public HashedString(long[] hashes, long[] modifiers) {
                    this.hashes = hashes;
                    this.modifiers = modifiers;
                }
            }

            long P;
            long M;

            public StringHasher() {
                initializePandM();
            }

            HashedString hashString(String s) {
                int n = s.length();
                long[] hashes = new long[n];
                long[] modifiers = new long[n];
                hashes[0] = s.charAt(0);
                modifiers[0] = 1;
                for (int i=1; i<n; i++) {
                    hashes[i] = (hashes[i-1] * P + s.charAt(i)) % M;
                    modifiers[i] = (modifiers[i-1] * P) % M;
                }
                return new HashedString(hashes, modifiers);
            }

            /**
             * Indices are inclusive.
             */
            long getHash(HashedString hashedString, int startIndex, int endIndex) {
                long[] hashes = hashedString.hashes;
                long[] modifiers = hashedString.modifiers;
                long result = hashes[endIndex];
                if (startIndex > 0) result -= (hashes[startIndex-1] * modifiers[endIndex-startIndex+1]) % M;
                if (result < 0) result += M;
                return result;
            }

            // Less interesting methods below

            /**
             * Efficient for 2 input parameter strings in particular.
             */
            HashedString[] hashString(String first, String second) {
                HashedString[] array = new HashedString[2];
                int n = first.length();
                long[] modifiers = new long[n];
                modifiers[0] = 1;

                long[] firstHashes = new long[n];
                firstHashes[0] = first.charAt(0);
                array[0] = new HashedString(firstHashes, modifiers);

                long[] secondHashes = new long[n];
                secondHashes[0] = second.charAt(0);
                array[1] = new HashedString(secondHashes, modifiers);

                for (int i=1; i<n; i++) {
                    modifiers[i] = (modifiers[i-1] * P) % M;
                    firstHashes[i] = (firstHashes[i-1] * P + first.charAt(i)) % M;
                    secondHashes[i] = (secondHashes[i-1] * P + second.charAt(i)) % M;
                }
                return array;
            }

            /**
             * Efficient for 3+ strings
             * More efficient than multiple hashString calls IF strings are same length.
             */
            HashedString[] hashString(String... strings) {
                HashedString[] array = new HashedString[strings.length];
                int n = strings[0].length();
                long[] modifiers = new long[n];
                modifiers[0] = 1;
                for (int j=0; j<strings.length; j++) {
                    // if all strings are not same length, defer work to another method
                    if (strings[j].length() != n) {
                        for (int i=0; i<n; i++) {
                            array[i] = hashString(strings[i]);
                        }
                        return array;
                    }

                    // otherwise initialize stuff
                    long[] hashes = new long[n];
                    hashes[0] = strings[j].charAt(0);
                    array[j] = new HashedString(hashes, modifiers);
                }
                for (int i=1; i<n; i++) {
                    modifiers[i] = (modifiers[i-1] * P) % M;
                    for (int j=0; j<strings.length; j++) {
                        String s = strings[j];
                        long[] hashes = array[j].hashes;
                        hashes[i] = (hashes[i-1] * P + s.charAt(i)) % M;
                    }
                }
                return array;
            }

            void initializePandM() {
                ArrayList<Long> modOptions = new ArrayList<>(20);
                modOptions.add(353873237L);
                modOptions.add(353875897L);
                modOptions.add(353878703L);
                modOptions.add(353882671L);
                modOptions.add(353885303L);
                modOptions.add(353888377L);
                modOptions.add(353893457L);
                P = modOptions.get(new Random().nextInt(modOptions.size()));

                modOptions.clear();
                modOptions.add(452940277L);
                modOptions.add(452947687L);
                modOptions.add(464478431L);
                modOptions.add(468098221L);
                modOptions.add(470374601L);
                modOptions.add(472879717L);
                modOptions.add(472881973L);
                M = modOptions.get(new Random().nextInt(modOptions.size()));

            }
        }

        int editDistance(String a, String b) {
            a = "#"+a;
            b = "#"+b;
            int n = a.length();
            int m = b.length();
            int[][] dp = new int[n+1][m+1];
            for (int y=0; y<=n; y++) {
                for (int x=0; x<=m; x++) {
                    if (y == 0) dp[y][x] = x;
                    else if (x == 0) dp[y][x] = y;
                    else {
                        int e1 = dp[y-1][x] + 1;
                        int e2 = dp[y][x-1] + 1;
                        int e3 = dp[y-1][x-1] + (a.charAt(y-1) != b.charAt(x-1) ? 1 : 0);
                        dp[y][x] = min(e1, e2, e3);
                    }
                }
            }
            return dp[n][m];
        }

        /*************************** Technical ***************************/

        private class IO extends PrintWriter {
            private InputStreamReader r;
            private static final int BUFSIZE = 1 << 15;
            private char[] buf;
            private int bufc;
            private int bufi;
            private StringBuilder sb;

            public IO() {
                super(new BufferedOutputStream(System.out));
                r = new InputStreamReader(System.in);

                buf = new char[BUFSIZE];
                bufc = 0;
                bufi = 0;
                sb = new StringBuilder();
            }

            /** Print, flush, return nextInt. */
            private int queryInt(String s) {
                io.println(s);
                io.flush();
                return nextInt();
            }

            /** Print, flush, return nextLong. */
            private long queryLong(String s) {
                io.println(s);
                io.flush();
                return nextLong();
            }

            /** Print, flush, return next word. */
            private String queryNext(String s) {
                io.println(s);
                io.flush();
                return next();
            }

            private void fillBuf() throws IOException {
                bufi = 0;
                bufc = 0;
                while(bufc == 0) {
                    bufc = r.read(buf, 0, BUFSIZE);
                    if(bufc == -1) {
                        bufc = 0;
                        return;
                    }
                }
            }

            private boolean pumpBuf() throws IOException {
                if(bufi == bufc) {
                    fillBuf();
                }
                return bufc != 0;
            }

            private boolean isDelimiter(char c) {
                return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f';
            }

            private void eatDelimiters() throws IOException {
                while(true) {
                    if(bufi == bufc) {
                        fillBuf();
                        if(bufc == 0) throw new RuntimeException("IO: Out of input.");
                    }

                    if(!isDelimiter(buf[bufi])) break;
                    ++bufi;
                }
            }

            public String next() {
                try {
                    sb.setLength(0);

                    eatDelimiters();
                    int start = bufi;

                    while(true) {
                        if(bufi == bufc) {
                            sb.append(buf, start, bufi - start);
                            fillBuf();
                            start = 0;
                            if(bufc == 0) break;
                        }

                        if(isDelimiter(buf[bufi])) break;
                        ++bufi;
                    }

                    sb.append(buf, start, bufi - start);

                    return sb.toString();
                } catch(IOException e) {
                    throw new RuntimeException("IO.next: Caught IOException.");
                }
            }

            public int nextInt() {
                try {
                    int ret = 0;

                    eatDelimiters();

                    boolean positive = true;
                    if(buf[bufi] == '-') {
                        ++bufi;
                        if(!pumpBuf()) throw new RuntimeException("IO.nextInt: Invalid int.");
                        positive = false;
                    }

                    boolean first = true;
                    while(true) {
                        if(!pumpBuf()) break;
                        if(isDelimiter(buf[bufi])) {
                            if(first) throw new RuntimeException("IO.nextInt: Invalid int.");
                            break;
                        }
                        first = false;

                        if(buf[bufi] >= '0' && buf[bufi] <= '9') {
                            if(ret < -214748364) throw new RuntimeException("IO.nextInt: Invalid int.");
                            ret *= 10;
                            ret -= (int)(buf[bufi] - '0');
                            if(ret > 0) throw new RuntimeException("IO.nextInt: Invalid int.");
                        } else {
                            throw new RuntimeException("IO.nextInt: Invalid int.");
                        }

                        ++bufi;
                    }

                    if(positive) {
                        if(ret == -2147483648) throw new RuntimeException("IO.nextInt: Invalid int.");
                        ret = -ret;
                    }

                    return ret;
                } catch(IOException e) {
                    throw new RuntimeException("IO.nextInt: Caught IOException.");
                }
            }

            public long nextLong() {
                try {
                    long ret = 0;

                    eatDelimiters();

                    boolean positive = true;
                    if(buf[bufi] == '-') {
                        ++bufi;
                        if(!pumpBuf()) throw new RuntimeException("IO.nextLong: Invalid long.");
                        positive = false;
                    }

                    boolean first = true;
                    while(true) {
                        if(!pumpBuf()) break;
                        if(isDelimiter(buf[bufi])) {
                            if(first) throw new RuntimeException("IO.nextLong: Invalid long.");
                            break;
                        }
                        first = false;

                        if(buf[bufi] >= '0' && buf[bufi] <= '9') {
                            if(ret < -922337203685477580L) throw new RuntimeException("IO.nextLong: Invalid long.");
                            ret *= 10;
                            ret -= (long)(buf[bufi] - '0');
                            if(ret > 0) throw new RuntimeException("IO.nextLong: Invalid long.");
                        } else {
                            throw new RuntimeException("IO.nextLong: Invalid long.");
                        }

                        ++bufi;
                    }

                    if(positive) {
                        if(ret == -9223372036854775808L) throw new RuntimeException("IO.nextLong: Invalid long.");
                        ret = -ret;
                    }

                    return ret;
                } catch(IOException e) {
                    throw new RuntimeException("IO.nextLong: Caught IOException.");
                }
            }

            public double nextDouble() {
                return Double.parseDouble(next());
            }

        }

        void print(Object output) {
            io.println(output);
        }

        void done(Object output) {
            print(output);
            done();
        }

        void done() {
            io.close();
            throw new RuntimeException("Clean exit");
        }

        long min(long... v) {
            long ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.min(ans, v[i]);
            }
            return ans;
        }

        double min(double... v) {
            double ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.min(ans, v[i]);
            }
            return ans;
        }

        int min(int... v) {
            int ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.min(ans, v[i]);
            }
            return ans;
        }

        long max(long... v) {
            long ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.max(ans, v[i]);
            }
            return ans;
        }

        double max(double... v) {
            double ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.max(ans, v[i]);
            }
            return ans;
        }

        int max(int... v) {
            int ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.max(ans, v[i]);
            }
            return ans;
        }

    }

}

//package baobab;

import java.io.*;
import java.util.*;

public class D {

    public static void main(String[] args) {
        Solver solver = new Solver();
    }

    static class Solver {
        IO io;

        public Solver() {
            this.io = new IO();
            try {
                solve();
            } catch (RuntimeException e) {
                if (!e.getMessage().equals("Clean exit")) {
                    throw e;
                }
            } finally {
                io.close();
            }
        }

        /****************************** START READING HERE ********************************/

        void solve() {
            int n = io.nextInt();
            int[][] b = new int[n][n];
            for (int y=0; y<n; y++) {
                for (int x=0; x<n; x++) {
                    b[y][x] = io.nextInt();
                }
            }
            int end = n*n;
            int[] x = new int[end+1];
            int[] y = new int[end+1];
            for (int i=0; i<n; i++) {
                for (int j=0; j<n; j++) {
                    int v = b[i][j];
                    y[v] = i;
                    x[v] = j;
                }
            }

            State ans = null;
            State[][][][][] leastSteps = new State[n][n][n][n][3];
            Deque<State> q = new ArrayDeque<>();
            q.add(new State(y[1], x[1], y[2], x[2], ROOK, 0, 0));
            q.add(new State(y[1], x[1], y[2], x[2], KNIGHT, 0, 0));
            q.add(new State(y[1], x[1], y[2], x[2], BISHOP, 0, 0));
            while (!q.isEmpty()) {
                State s = q.pollFirst();
                if (s.currY < 0 || s.currX < 0 || s.currY >= n || s.currX >= n) continue;
                State prev = leastSteps[s.currY][s.currX][s.goalY][s.goalX][s.piece];
                if (prev != null) {
                    if (prev.steps < s.steps) continue;
                    if (prev.steps == s.steps && prev.replacements <= s.replacements) continue;
                }
                leastSteps[s.currY][s.currX][s.goalY][s.goalX][s.piece] = s;

                // intermediary goal reached?
                if (s.currX == s.goalX && s.currY == s.goalY) {

                    if (b[s.currY][s.currX] == end) {
                        // actually reached end state with fewest steps but not necessarily fewest replacements for that step size
                        if (ans == null) ans = s;
                        else if (ans.replacements >= s.replacements) ans = s;
                        continue; // stop bfs but dont break because we may find new solution with same steps but fewer replacements
                    }

                    // change state to set next goal.
                    int prevGoal = b[s.goalY][s.goalX];
                    int newGoal = prevGoal + 1;
                    int newGoalX = x[newGoal];
                    int newGoalY = y[newGoal];
                    // front of the queue!
                    q.addFirst(new State(s.goalY, s.goalX, newGoalY, newGoalX, s.piece, s.steps, s.replacements));
                    continue;
                }

                // change piece?
                q.addLast(new State(s.currY, s.currX, s.goalY, s.goalX, ROOK, s.steps+1, s.replacements+1));
                q.addLast(new State(s.currY, s.currX, s.goalY, s.goalX, BISHOP, s.steps+1, s.replacements+1));
                q.addLast(new State(s.currY, s.currX, s.goalY, s.goalX, KNIGHT, s.steps+1, s.replacements+1));

                // move current piece?
                if (s.piece == ROOK) {
                    for (int newY=0; newY<n; newY++) q.addLast(new State(newY, s.currX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    for (int newX=0; newX<n; newX++) q.addLast(new State(s.currY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                } else if (s.piece == KNIGHT) {
                    int newY = s.currY + 1;
                    int newX = s.currX + 2;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY + 1;
                    newX = s.currX - 2;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY - 1;
                    newX = s.currX - 2;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY - 1;
                    newX = s.currX + 2;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY + 2;
                    newX = s.currX + 1;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY - 2;
                    newX = s.currX + 1;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY + 2;
                    newX = s.currX - 1;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY - 2;
                    newX = s.currX - 1;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));

                } else if (s.piece == BISHOP) {
                    int newY = s.currY;
                    int newX = s.currX;
                    for (int diff=1 ;; diff++) {
                        newY += diff;
                        newX += diff;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY;
                    newX = s.currX;
                    for (int diff=1 ;; diff++) {
                        newY += diff;
                        newX -= diff;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY;
                    newX = s.currX;
                    for (int diff=1 ;; diff++) {
                        newY -= diff;
                        newX -= diff;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY;
                    newX = s.currX;
                    for (int diff=1 ;; diff++) {
                        newY -= diff;
                        newX += diff;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                }
            }

            io.println(ans.steps + " " + ans.replacements);
        }

        static int ROOK = 0;
        static int KNIGHT = 1;
        static int BISHOP = 2;

        class State {
            int currY;
            int currX;
            int goalY;
            int goalX;
            int piece;
            int steps;
            int replacements;

            public State(int currY, int currX, int goalY, int goalX, int piece, int steps, int replacements) {
                this.currY = currY;
                this.currX = currX;
                this.goalY = goalY;
                this.goalX = goalX;
                this.piece = piece;
                this.steps = steps;
                this.replacements = replacements;
            }

            @Override
            public boolean equals(Object o) {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;
                State state = (State) o;
                return currY == state.currY &&
                        currX == state.currX &&
                        goalY == state.goalY &&
                        goalX == state.goalX &&
                        piece == state.piece;
            }

            @Override
            public int hashCode() {
                return Objects.hash(currY, currX, goalY, goalX, piece);
            }
        }

        /************************** UTILITY CODE BELOW THIS LINE **************************/

        long MOD = (long)1e9 + 7;

        boolean closeToZero(double v) {
            // Check if double is close to zero, considering precision issues.
            return Math.abs(v) <= 0.0000000001;
        }

        class DrawGrid {

            void draw(boolean[][] d) {
                System.out.print("  ");
                for (int x=0; x<d[0].length; x++) {
                    System.out.print(" " + x + " ");
                }
                System.out.println("");
                for (int y=0; y<d.length; y++) {
                    System.out.print(y + " ");
                    for (int x=0; x<d[0].length; x++) {
                        System.out.print((d[y][x] ? "[x]" : "[ ]"));
                    }
                    System.out.println("");
                }
            }

            void draw(int[][] d) {
                int max = 1;
                for (int y=0; y<d.length; y++) {
                    for (int x=0; x<d[0].length; x++) {
                        max = Math.max(max, ("" + d[y][x]).length());
                    }
                }
                System.out.print("  ");
                String format = "%" + (max+2) + "s";
                for (int x=0; x<d[0].length; x++) {
                    System.out.print(String.format(format, x) + " ");
                }
                format = "%" + (max) + "s";
                System.out.println("");
                for (int y=0; y<d.length; y++) {
                    System.out.print(y + " ");
                    for (int x=0; x<d[0].length; x++) {
                        System.out.print(" [" + String.format(format, (d[y][x])) + "]");
                    }
                    System.out.println("");
                }
            }

        }

        class IDval implements Comparable<IDval> {
            int steps;
            long replacements;

            public IDval(int steps, long replacements) {
                this.replacements = replacements;
                this.steps = steps;
            }

            @Override
            public int compareTo(IDval o) {
                if (this.replacements < o.replacements) return -1;
                if (this.replacements > o.replacements) return 1;
                return this.steps - o.steps;
            }
        }

        private class ElementCounter {
            private HashMap<Long, Integer> elements;

            public ElementCounter() {
                elements = new HashMap<>();
            }

            public void add(long element) {
                int count = 1;
                Integer prev = elements.get(element);
                if (prev != null) count += prev;
                elements.put(element, count);
            }

            public void remove(long element) {
                int count = elements.remove(element);
                count--;
                if (count > 0) elements.put(element, count);
            }

            public int get(long element) {
                Integer val = elements.get(element);
                if (val == null) return 0;
                return val;
            }

            public int size() {
                return elements.size();
            }
        }

        class StringCounter {
            HashMap<String, Integer> elements;

            public StringCounter() {
                elements = new HashMap<>();
            }

            public void add(String identifier) {
                int count = 1;
                Integer prev = elements.get(identifier);
                if (prev != null) count += prev;
                elements.put(identifier, count);
            }

            public void remove(String identifier) {
                int count = elements.remove(identifier);
                count--;
                if (count > 0) elements.put(identifier, count);
            }

            public long get(String identifier) {
                Integer val = elements.get(identifier);
                if (val == null) return 0;
                return val;
            }

            public int size() {
                return elements.size();
            }
        }

        class DisjointSet {
            /** Union Find / Disjoint Set data structure. */
            int[] size;
            int[] parent;
            int componentCount;

            public DisjointSet(int n) {
                componentCount = n;
                size = new int[n];
                parent = new int[n];
                for (int i=0; i<n; i++) parent[i] = i;
                for (int i=0; i<n; i++) size[i] = 1;
            }

            public void join(int a, int b) {
                /* Find roots */
                int rootA = parent[a];
                int rootB = parent[b];
                while (rootA != parent[rootA]) rootA = parent[rootA];
                while (rootB != parent[rootB]) rootB = parent[rootB];

                if (rootA == rootB) {
                    /* Already in the same set */
                    return;
                }

                /* Merge smaller set into larger set. */
                if (size[rootA] > size[rootB]) {
                    size[rootA] += size[rootB];
                    parent[rootB] = rootA;
                } else {
                    size[rootB] += size[rootA];
                    parent[rootA] = rootB;
                }
                componentCount--;
            }

        }

        class Trie {
            int N;
            int Z;
            int nextFreeId;
            int[][] pointers;
            boolean[] end;

            /** maxLenSum = maximum possible sum of length of words */
            public Trie(int maxLenSum, int alphabetSize) {
                this.N = maxLenSum;
                this.Z = alphabetSize;
                this.nextFreeId = 1;
                pointers = new int[N+1][alphabetSize];
                end = new boolean[N+1];
            }

            public void addWord(String word) {
                int curr = 0;
                for (int j=0; j<word.length(); j++) {
                    int c = word.charAt(j) - 'a';
                    int next = pointers[curr][c];
                    if (next == 0) {
                        next = nextFreeId++;
                        pointers[curr][c] = next;
                    }
                    curr = next;
                }
                end[curr] = true;
            }

            public boolean hasWord(String word) {
                int curr = 0;
                for (int j=0; j<word.length(); j++) {
                    int c = word.charAt(j) - 'a';
                    int next = pointers[curr][c];
                    if (next == 0) return false;
                    curr = next;
                }
                return end[curr];
            }

        }

        private static class Prob {

            /** For heavy calculations on probabilities, this class
             *  provides more accuracy & efficiency than doubles.
             *  Math explained: https://en.wikipedia.org/wiki/Log_probability
             *  Quick start:
             *      - Instantiate probabilities, eg. Prob a = new Prob(0.75)
             *      - add(), multiply() return new objects, can perform on nulls & NaNs.
             *      - get() returns probability as a readable double */

            /** Logarithmized probability. Note: 0% represented by logP NaN. */
            private double logP;

            /** Construct instance with real probability. */
            public Prob(double real) {
                if (real > 0) this.logP = Math.log(real);
                else this.logP = Double.NaN;
            }

            /** Construct instance with already logarithmized value. */
            static boolean dontLogAgain = true;
            public Prob(double logP, boolean anyBooleanHereToChooseThisConstructor) {
                this.logP = logP;
            }

            /** Returns real probability as a double. */
            public double get() {
                return Math.exp(logP);
            }

            @Override
            public String toString() {
                return ""+get();
            }

            /***************** STATIC METHODS BELOW ********************/

            /** Note: returns NaN only when a && b are both NaN/null. */
            public static Prob add(Prob a, Prob b) {
                if (nullOrNaN(a) && nullOrNaN(b)) return new Prob(Double.NaN, dontLogAgain);
                if (nullOrNaN(a)) return copy(b);
                if (nullOrNaN(b)) return copy(a);

                double x = Math.max(a.logP, b.logP);
                double y = Math.min(a.logP, b.logP);
                double sum = x + Math.log(1 + Math.exp(y - x));
                return new Prob(sum, dontLogAgain);
            }

            /** Note: multiplying by null or NaN produces NaN (repping 0% real prob). */
            public static Prob multiply(Prob a, Prob b) {
                if (nullOrNaN(a) || nullOrNaN(b)) return new Prob(Double.NaN, dontLogAgain);
                return new Prob(a.logP + b.logP, dontLogAgain);
            }

            /** Returns true if p is null or NaN. */
            private static boolean nullOrNaN(Prob p) {
                return (p == null || Double.isNaN(p.logP));
            }

            /** Returns a new instance with the same value as original. */
            private static Prob copy(Prob original) {
                return new Prob(original.logP, dontLogAgain);
            }
        }

        class Binary implements Comparable<Binary> {

            /**
             * Use example: Binary b = new Binary(Long.toBinaryString(53249834L));
             *
             * When manipulating small binary strings, instantiate new Binary(string)
             * When just reading large binary strings, instantiate new Binary(string,true)
             * get(int i) returns a character '1' or '0', not an int.
             */

            private boolean[] d;
            private int first; // Starting from left, the first (most remarkable) '1'
            public int length;

            public Binary(String binaryString) {
                this(binaryString, false);
            }
            public Binary(String binaryString, boolean initWithMinArraySize) {
                length = binaryString.length();
                int size = Math.max(2*length, 1);
                first = length/4;
                if (initWithMinArraySize) {
                    first = 0;
                    size = Math.max(length, 1);
                }
                d = new boolean[size];
                for (int i=0; i<length; i++) {
                    if (binaryString.charAt(i) == '1') d[i+first] = true;
                }
            }

            public void addFirst(char c) {
                if (first-1 < 0) doubleArraySize();
                first--;
                d[first] = (c == '1' ? true : false);
                length++;
            }

            public void addLast(char c) {
                if (first+length >= d.length) doubleArraySize();
                d[first+length] = (c == '1' ? true : false);
                length++;
            }

            private void doubleArraySize() {
                boolean[] bigArray = new boolean[(d.length+1) * 2];
                int newFirst = bigArray.length / 4;
                for (int i=0; i<length; i++) {
                    bigArray[i + newFirst] = d[i + first];
                }
                first = newFirst;
                d = bigArray;
            }

            public boolean flip(int i) {
                boolean value = (this.d[first+i] ? false : true);
                this.d[first+i] = value;
                return value;
            }

            public void set(int i, char c) {
                boolean value = (c == '1' ? true : false);
                this.d[first+i] = value;
            }

            public char get(int i) {
                return (this.d[first+i] ? '1' : '0');
            }

            @Override
            public int compareTo(Binary o) {
                if (this.length != o.length) return this.length - o.length;
                int len = this.length;
                for (int i=0; i<len; i++) {
                    int diff = this.get(i) - o.get(i);
                    if (diff != 0) return diff;
                }
                return 0;
            }

            @Override
            public String toString() {
                StringBuilder sb = new StringBuilder();
                for (int i=0; i<length; i++) {
                    sb.append(d[i+first] ? '1' : '0');
                }
                return sb.toString();
            }

        }

        /************************** Range queries **************************/

        class FenwickMin {
            long n;
            long[] original;
            long[] bottomUp;
            long[] topDown;

            public FenwickMin(int n) {
                this.n = n;
                original = new long[n+2];
                bottomUp = new long[n+2];
                topDown = new long[n+2];
            }

            public void set(int modifiedNode, long value) {
                long replaced = original[modifiedNode];
                original[modifiedNode] = value;
                // Update left tree
                int i = modifiedNode;
                long v = value;
                while (i <= n) {
                    if (v > bottomUp[i]) {
                        if (replaced == bottomUp[i]) {
                            v = Math.min(v, original[i]);
                            for (int r=1 ;; r++) {
                                int x = (i&-i)>>>r;
                                if (x == 0) break;
                                int child = i-x;
                                v = Math.min(v, bottomUp[child]);
                            }
                        } else break;
                    }
                    if (v == bottomUp[i]) break;
                    bottomUp[i] = v;
                    i += (i&-i);
                }
                // Update right tree
                i = modifiedNode;
                v = value;
                while (i > 0) {
                    if (v > topDown[i]) {
                        if (replaced == topDown[i]) {
                            v = Math.min(v, original[i]);
                            for (int r=1 ;; r++) {
                                int x = (i&-i)>>>r;
                                if (x == 0) break;
                                int child = i+x;
                                if (child > n+1) break;
                                v = Math.min(v, topDown[child]);
                            }
                        } else break;
                    }
                    if (v == topDown[i]) break;
                    topDown[i] = v;
                    i -= (i&-i);
                }
            }

            public long getMin(int a, int b) {
                long min = original[a];
                int prev = a;
                int curr = prev + (prev&-prev); // parent right hand side
                while (curr <= b) {
                    min = Math.min(min, topDown[prev]); // value from the other tree
                    prev = curr;
                    curr = prev + (prev&-prev);;
                }
                min = Math.min(min, original[prev]);
                prev = b;
                curr = prev - (prev&-prev); // parent left hand side
                while (curr >= a) {
                    min = Math.min(min,bottomUp[prev]); // value from the other tree
                    prev = curr;
                    curr = prev - (prev&-prev);
                }
                return min;
            }

        }

        class FenwickSum {
            public long[] d;

            public FenwickSum(int n) {
                d=new long[n+1];
            }

            /** a[0] must be unused. */
            public FenwickSum(long[] a) {
                d=new long[a.length];
                for (int i=1; i<a.length; i++) {
                    modify(i, a[i]);
                }
            }

            /** Do not modify i=0. */
            void modify(int i, long v) {
                while (i<d.length) {
                    d[i] += v;
                    // Move to next uplink on the RIGHT side of i
                    i += (i&-i);
                }
            }

            /** Returns sum from a to b, *BOTH* inclusive. */
            long getSum(int a, int b) {
                return getSum(b) - getSum(a-1);
            }

            private long getSum(int i) {
                long sum = 0;
                while (i>0) {
                    sum += d[i];
                    // Move to next uplink on the LEFT side of i
                    i -= (i&-i);
                }
                return sum;
            }
        }

        class SegmentTree {

            /* Provides log(n) operations for:
             * - Range query (sum, min or max)
             * - Range update ("+8 to all values between indexes 4 and 94")
             */

            int N;
            long[] lazy;
            long[] sum;
            long[] min;
            long[] max;
            boolean supportSum;
            boolean supportMin;
            boolean supportMax;

            public SegmentTree(int n) {
                this(n, true, true, true);
            }

            public SegmentTree(int n, boolean supportSum, boolean supportMin, boolean supportMax) {
                for (N=2; N<n;) N*=2;
                this.lazy = new long[2*N];
                this.supportSum = supportSum;
                this.supportMin = supportMin;
                this.supportMax = supportMax;
                if (this.supportSum) this.sum = new long[2*N];
                if (this.supportMin) this.min = new long[2*N];
                if (this.supportMax) this.max = new long[2*N];
            }

            void modifyRange(long x, int a, int b) {
                modifyRec(a, b, 1, 0, N-1, x);
            }

            void setRange() {
                //TODO
            }

            long getSum(int a, int b) {
                return querySum(a, b, 1, 0, N-1);
            }

            long getMin(int a, int b) {
                return queryMin(a, b, 1, 0, N-1);
            }

            long getMax(int a, int b) {
                return queryMax(a, b, 1, 0, N-1);
            }

            private long querySum(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return 0;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    int count = wantedRight - wantedLeft + 1;
                    return sum[i] + count * lazy[i];
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                long left = querySum(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1);
                long right = querySum(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight);
                return left + right;
            }

            private long queryMin(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return 0;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    return min[i] + lazy[i];
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                long left = queryMin(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1);
                long right = queryMin(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight);
                return min(left, right);
            }

            private long queryMax(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return 0;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    return max[i] + lazy[i];
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                long left = queryMax(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1);
                long right = queryMax(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight);
                return max(left, right);
            }

            private void modifyRec(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight, long value) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    lazy[i] += value;
                    return;
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                modifyRec(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1, value);
                modifyRec(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight, value);
                if (supportSum) sum[i] += value * (min(actualRight, wantedRight) - max(actualLeft, wantedLeft) + 1);
                if (supportMin) min[i] = min(min[2*i] + lazy[2*i], min[2*i+1] + lazy[2*i+1]);
                if (supportMax) max[i] = max(max[2*i] + lazy[2*i], max[2*i+1] + lazy[2*i+1]);
            }

            private void propagate(int i, int actualLeft, int actualRight) {
                lazy[2*i] += lazy[i];
                lazy[2*i+1] += lazy[i];
                if (supportSum) sum[i] += lazy[i] * (actualRight - actualLeft + 1);
                if (supportMin) min[i] += lazy[i];
                if (supportMax) max[i] += lazy[i];
                lazy[i] = 0;
            }

        }

        /***************************** Graphs *****************************/

        List<Integer>[] toGraph(IO io, int n) {
            List<Integer>[] g = new ArrayList[n+1];
            for (int i=1; i<=n; i++) g[i] = new ArrayList<>();
            for (int i=1; i<=n-1; i++) {
                int a = io.nextInt();
                int b = io.nextInt();
                g[a].add(b);
                g[b].add(a);
            }
            return g;
        }

        class Graph {
            HashMap<Long, List<Long>> edges;

            public Graph() {
                edges = new HashMap<>();
            }

            List<Long> getSetNeighbors(Long node) {
                List<Long> neighbors = edges.get(node);
                if (neighbors == null) {
                    neighbors = new ArrayList<>();
                    edges.put(node, neighbors);
                }
                return neighbors;
            }

            void addBiEdge(Long a, Long b) {
                addEdge(a, b);
                addEdge(b, a);
            }

            void addEdge(Long from, Long to) {
                getSetNeighbors(to); // make sure all have initialized lists
                List<Long> neighbors = getSetNeighbors(from);
                neighbors.add(to);
            }

            // topoSort variables
            int UNTOUCHED = 0;
            int FINISHED = 2;
            int INPROGRESS = 1;
            HashMap<Long, Integer> vis;
            List<Long> topoAns;
            List<Long> failDueToCycle = new ArrayList<Long>() {{ add(-1L); }};

            List<Long> topoSort() {
                topoAns = new ArrayList<>();
                vis = new HashMap<>();
                for (Long a : edges.keySet()) {
                    if (!topoDFS(a)) return failDueToCycle;
                }
                Collections.reverse(topoAns);
                return topoAns;
            }

            boolean topoDFS(long curr) {
                Integer status = vis.get(curr);
                if (status == null) status = UNTOUCHED;
                if (status == FINISHED) return true;
                if (status == INPROGRESS) {
                    return false;
                }
                vis.put(curr, INPROGRESS);
                for (long next : edges.get(curr)) {
                    if (!topoDFS(next)) return false;
                }
                vis.put(curr, FINISHED);
                topoAns.add(curr);
                return true;
            }

        }

        public class StronglyConnectedComponents {

            /** Kosaraju's algorithm */

            ArrayList<Integer>[] forw;
            ArrayList<Integer>[] bacw;

            /** Use: getCount(2, new int[] {1,2}, new int[] {2,1}) */
            public int getCount(int n, int[] mista, int[] minne) {
                forw = new ArrayList[n+1];
                bacw = new ArrayList[n+1];
                for (int i=1; i<=n; i++) {
                    forw[i] = new ArrayList<Integer>();
                    bacw[i] = new ArrayList<Integer>();
                }
                for (int i=0; i<mista.length; i++) {
                    int a = mista[i];
                    int b = minne[i];
                    forw[a].add(b);
                    bacw[b].add(a);
                }
                int count = 0;
                List<Integer> list = new ArrayList<Integer>();
                boolean[] visited = new boolean[n+1];
                for (int i=1; i<=n; i++) {
                    dfsForward(i, visited, list);
                }
                visited = new boolean[n+1];
                for (int i=n-1; i>=0; i--) {
                    int node = list.get(i);
                    if (visited[node]) continue;
                    count++;
                    dfsBackward(node, visited);
                }
                return count;
            }

            public void dfsForward(int i, boolean[] visited, List<Integer> list) {
                if (visited[i]) return;
                visited[i] = true;
                for (int neighbor : forw[i]) {
                    dfsForward(neighbor, visited, list);
                }
                list.add(i);
            }

            public void dfsBackward(int i, boolean[] visited) {
                if (visited[i]) return;
                visited[i] = true;
                for (int neighbor : bacw[i]) {
                    dfsBackward(neighbor, visited);
                }
            }
        }

        class LCAFinder {

            /* O(n log n) Initialize: new LCAFinder(graph)
             * O(log n) Queries: find(a,b) returns lowest common ancestor for nodes a and b */

            int[] nodes;
            int[] depths;
            int[] entries;
            int pointer;
            FenwickMin fenwick;

            public LCAFinder(List<Integer>[] graph) {
                this.nodes = new int[(int)10e6];
                this.depths = new int[(int)10e6];
                this.entries = new int[graph.length];
                this.pointer = 1;
                boolean[] visited = new boolean[graph.length+1];
                dfs(1, 0, graph, visited);
                fenwick = new FenwickMin(pointer-1);
                for (int i=1; i<pointer; i++) {
                    fenwick.set(i, depths[i] * 1000000L + i);
                }
            }

            private void dfs(int node, int depth, List<Integer>[] graph, boolean[] visited) {
                visited[node] = true;
                entries[node] = pointer;
                nodes[pointer] = node;
                depths[pointer] = depth;
                pointer++;
                for (int neighbor : graph[node]) {
                    if (visited[neighbor]) continue;
                    dfs(neighbor, depth+1, graph, visited);
                    nodes[pointer] = node;
                    depths[pointer] = depth;
                    pointer++;
                }
            }

            public int find(int a, int b) {
                int left = entries[a];
                int right = entries[b];
                if (left > right) {
                    int temp = left;
                    left = right;
                    right = temp;
                }
                long mixedBag = fenwick.getMin(left, right);
                int index = (int) (mixedBag % 1000000L);
                return nodes[index];
            }
        }

        /**************************** Geometry ****************************/

        class Point {
            int y;
            int x;

            public Point(int y, int x) {
                this.y = y;
                this.x = x;
            }
        }

        boolean segmentsIntersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
            // Returns true if segment 1-2 intersects segment 3-4

            if (x1 == x2 && x3 == x4) {
                // Both segments are vertical
                if (x1 != x3) return false;
                if (min(y1,y2) < min(y3,y4)) {
                    return max(y1,y2) >= min(y3,y4);
                } else {
                    return max(y3,y4) >= min(y1,y2);
                }
            }
            if (x1 == x2) {
                // Only segment 1-2 is vertical. Does segment 3-4 cross it? y = a*x + b
                double a34 = (y4-y3)/(x4-x3);
                double b34 = y3 - a34*x3;
                double y = a34 * x1 + b34;
                return y >= min(y1,y2) && y <= max(y1,y2) && x1 >= min(x3,x4) && x1 <= max(x3,x4);
            }
            if (x3 == x4) {
                // Only segment 3-4 is vertical. Does segment 1-2 cross it? y = a*x + b
                double a12 = (y2-y1)/(x2-x1);
                double b12 = y1 - a12*x1;
                double y = a12 * x3 + b12;
                return y >= min(y3,y4) && y <= max(y3,y4) && x3 >= min(x1,x2) && x3 <= max(x1,x2);
            }
            double a12 = (y2-y1)/(x2-x1);
            double b12 = y1 - a12*x1;
            double a34 = (y4-y3)/(x4-x3);
            double b34 = y3 - a34*x3;
            if (closeToZero(a12 - a34)) {
                // Parallel lines
                return closeToZero(b12 - b34);
            }
            // Non parallel non vertical lines intersect at x. Is x part of both segments?
            double x = -(b12-b34)/(a12-a34);
            return x >= min(x1,x2) && x <= max(x1,x2) && x >= min(x3,x4) && x <= max(x3,x4);
        }

        boolean pointInsideRectangle(Point p, List<Point> r, boolean countBorderAsInside) {
            Point a = r.get(0);
            Point b = r.get(1);
            Point c = r.get(2);
            Point d = r.get(3);
            double apd = areaOfTriangle(a, p, d);
            double dpc = areaOfTriangle(d, p, c);
            double cpb = areaOfTriangle(c, p, b);
            double pba = areaOfTriangle(p, b, a);
            double sumOfAreas = apd + dpc + cpb + pba;
            if (closeToZero(sumOfAreas - areaOfRectangle(r))) {
                if (closeToZero(apd) || closeToZero(dpc) || closeToZero(cpb) || closeToZero(pba)) {
                    return countBorderAsInside;
                }
                return true;
            }
            return false;
        }

        double areaOfTriangle(Point a, Point b, Point c) {
            return 0.5 * Math.abs((a.x-c.x)*(b.y-a.y)-(a.x-b.x)*(c.y-a.y));
        }

        double areaOfRectangle(List<Point> r) {
            double side1xDiff = r.get(0).x - r.get(1).x;
            double side1yDiff = r.get(0).y - r.get(1).y;
            double side2xDiff = r.get(1).x - r.get(2).x;
            double side2yDiff = r.get(1).y - r.get(2).y;
            double side1 = Math.sqrt(side1xDiff * side1xDiff + side1yDiff * side1yDiff);
            double side2 = Math.sqrt(side2xDiff * side2xDiff + side2yDiff * side2yDiff);
            return side1 * side2;
        }

        boolean pointsOnSameLine(double x1, double y1, double x2, double y2, double x3, double y3) {
            double areaTimes2 = x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2);
            return (closeToZero(areaTimes2));
        }

        class PointToLineSegmentDistanceCalculator {

            // Just call this
            double minDistFromPointToLineSegment(double point_x, double point_y, double x1, double y1, double x2, double y2) {
                return Math.sqrt(distToSegmentSquared(point_x, point_y, x1, y1, x2, y2));
            }

            private double distToSegmentSquared(double point_x, double point_y, double x1, double y1, double x2, double y2) {
                double l2 = dist2(x1,y1,x2,y2);
                if (l2 == 0) return dist2(point_x, point_y, x1, y1);
                double t = ((point_x - x1) * (x2 - x1) + (point_y - y1) * (y2 - y1)) / l2;
                if (t < 0) return dist2(point_x, point_y, x1, y1);
                if (t > 1) return dist2(point_x, point_y, x2, y2);

                double com_x = x1 + t * (x2 - x1);
                double com_y = y1 + t * (y2 - y1);
                return dist2(point_x, point_y, com_x, com_y);
            }

            private double dist2(double x1, double y1, double x2, double y2) {
                return Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2);
            }

        }

        /****************************** Math ******************************/

        long pow(long base, int exp) {
            if (exp == 0) return 1L;
            long x = pow(base, exp/2);
            long ans = x * x;
            if (exp % 2 != 0) ans *= base;
            return ans;
        }

        long gcd(long... v) {
            /** Chained calls to Euclidean algorithm. */
            if (v.length == 1) return v[0];
            long ans = gcd(v[1], v[0]);
            for (int i=2; i<v.length; i++) {
                ans = gcd(ans, v[i]);
            }
            return ans;
        }

        long gcd(long a, long b) {
            /** Euclidean algorithm. */
            if (b == 0) return a;
            return gcd(b, a%b);
        }

        int[] generatePrimesUpTo(int last) {
            /* Sieve of Eratosthenes. Practically O(n). Values of 0 indicate primes. */
            int[] div = new int[last+1];
            for (int x=2; x<=last; x++) {
                if (div[x] > 0) continue;
                for (int u=2*x; u<=last; u+=x) {
                    div[u] = x;
                }
            }
            return div;
        }

        long lcm(long a, long b) {
            /** Least common multiple */
            return a * b / gcd(a,b);
        }

        class BaseConverter {

            /* Palauttaa luvun esityksen kannassa base */
            public String convert(Long number, int base) {
                return Long.toString(number, base);
            }

            /* Palauttaa luvun esityksen kannassa baseTo, kun annetaan luku Stringinä kannassa baseFrom */
            public String convert(String number, int baseFrom, int baseTo) {
                return Long.toString(Long.parseLong(number, baseFrom), baseTo);
            }

            /* Tulkitsee kannassa base esitetyn luvun longiksi (kannassa 10) */
            public long longify(String number, int baseFrom) {
                return Long.parseLong(number, baseFrom);
            }
        }

        class BinomialCoefficients {
            /** Total number of K sized unique combinations from pool of size N (unordered)
             N! / ( K! (N - K)! )   */

            /** For simple queries where output fits in long. */
            public long biCo(long n, long k) {
                long r = 1;
                if (k > n) return 0;
                for (long d = 1; d <= k; d++) {
                    r *= n--;
                    r /= d;
                }
                return r;
            }

            /** For multiple queries with same n, different k. */
            public long[] precalcBinomialCoefficientsK(int n, int maxK) {
                long v[] = new long[maxK+1];
                v[0] = 1; // nC0 == 1
                for (int i=1; i<=n; i++) {
                    for (int j=Math.min(i,maxK); j>0; j--) {
                        v[j] = v[j] + v[j-1]; // Pascal's triangle
                    }
                }
                return v;
            }

            /** When output needs % MOD. */
            public long[] precalcBinomialCoefficientsK(int n, int k, long M) {
                long v[] = new long[k+1];
                v[0] = 1; // nC0 == 1
                for (int i=1; i<=n; i++) {
                    for (int j=Math.min(i,k); j>0; j--) {
                        v[j] = v[j] + v[j-1]; // Pascal's triangle
                        v[j] %= M;
                    }
                }
                return v;
            }
        }

        /**************************** Strings ****************************/

        class Zalgo {

            public int pisinEsiintyma(String haku, String kohde) {
                char[] s = new char[haku.length() + 1 + kohde.length()];
                for (int i=0; i<haku.length(); i++) {
                    s[i] = haku.charAt(i);
                }
                int j = haku.length();
                s[j++] = '#';
                for (int i=0; i<kohde.length(); i++) {
                    s[j++] = kohde.charAt(i);
                }
                int[] z = toZarray(s);
                int max = 0;
                for (int i=haku.length(); i<z.length; i++) {
                    max = Math.max(max, z[i]);
                }
                return max;
            }

            public int[] toZarray(char[] s) {
                int n = s.length;
                int[] z = new int[n];
                int a = 0, b = 0;
                for (int i = 1; i < n; i++) {
                    if (i > b) {
                        for (int j = i; j < n && s[j - i] == s[j]; j++)  z[i]++;
                    }
                    else {
                        z[i] = z[i - a];
                        if (i + z[i - a] > b) {
                            for (int j = b + 1; j < n && s[j - i] == s[j]; j++) z[i]++;
                            a = i;
                            b = i + z[i] - 1;
                        }
                    }
                }
                return z;
            }

            public List<Integer> getStartIndexesWhereWordIsFound(String haku, String kohde) {
                // this is alternative use case
                char[] s = new char[haku.length() + 1 + kohde.length()];
                for (int i=0; i<haku.length(); i++) {
                    s[i] = haku.charAt(i);
                }
                int j = haku.length();
                s[j++] = '#';
                for (int i=0; i<kohde.length(); i++) {
                    s[j++] = kohde.charAt(i);
                }
                int[] z = toZarray(s);
                List<Integer> indexes = new ArrayList<>();
                for (int i=haku.length(); i<z.length; i++) {
                    if (z[i] < haku.length()) continue;
                    indexes.add(i);
                }
                return indexes;
            }

        }

        class StringHasher {

            class HashedString {
                long[] hashes;
                long[] modifiers;

                public HashedString(long[] hashes, long[] modifiers) {
                    this.hashes = hashes;
                    this.modifiers = modifiers;
                }
            }

            long P;
            long M;

            public StringHasher() {
                initializePandM();
            }

            HashedString hashString(String s) {
                int n = s.length();
                long[] hashes = new long[n];
                long[] modifiers = new long[n];
                hashes[0] = s.charAt(0);
                modifiers[0] = 1;
                for (int i=1; i<n; i++) {
                    hashes[i] = (hashes[i-1] * P + s.charAt(i)) % M;
                    modifiers[i] = (modifiers[i-1] * P) % M;
                }
                return new HashedString(hashes, modifiers);
            }

            /**
             * Indices are inclusive.
             */
            long getHash(HashedString hashedString, int startIndex, int endIndex) {
                long[] hashes = hashedString.hashes;
                long[] modifiers = hashedString.modifiers;
                long result = hashes[endIndex];
                if (startIndex > 0) result -= (hashes[startIndex-1] * modifiers[endIndex-startIndex+1]) % M;
                if (result < 0) result += M;
                return result;
            }

            // Less interesting methods below

            /**
             * Efficient for 2 input parameter strings in particular.
             */
            HashedString[] hashString(String first, String second) {
                HashedString[] array = new HashedString[2];
                int n = first.length();
                long[] modifiers = new long[n];
                modifiers[0] = 1;

                long[] firstHashes = new long[n];
                firstHashes[0] = first.charAt(0);
                array[0] = new HashedString(firstHashes, modifiers);

                long[] secondHashes = new long[n];
                secondHashes[0] = second.charAt(0);
                array[1] = new HashedString(secondHashes, modifiers);

                for (int i=1; i<n; i++) {
                    modifiers[i] = (modifiers[i-1] * P) % M;
                    firstHashes[i] = (firstHashes[i-1] * P + first.charAt(i)) % M;
                    secondHashes[i] = (secondHashes[i-1] * P + second.charAt(i)) % M;
                }
                return array;
            }

            /**
             * Efficient for 3+ strings
             * More efficient than multiple hashString calls IF strings are same length.
             */
            HashedString[] hashString(String... strings) {
                HashedString[] array = new HashedString[strings.length];
                int n = strings[0].length();
                long[] modifiers = new long[n];
                modifiers[0] = 1;
                for (int j=0; j<strings.length; j++) {
                    // if all strings are not same length, defer work to another method
                    if (strings[j].length() != n) {
                        for (int i=0; i<n; i++) {
                            array[i] = hashString(strings[i]);
                        }
                        return array;
                    }

                    // otherwise initialize stuff
                    long[] hashes = new long[n];
                    hashes[0] = strings[j].charAt(0);
                    array[j] = new HashedString(hashes, modifiers);
                }
                for (int i=1; i<n; i++) {
                    modifiers[i] = (modifiers[i-1] * P) % M;
                    for (int j=0; j<strings.length; j++) {
                        String s = strings[j];
                        long[] hashes = array[j].hashes;
                        hashes[i] = (hashes[i-1] * P + s.charAt(i)) % M;
                    }
                }
                return array;
            }

            void initializePandM() {
                ArrayList<Long> modOptions = new ArrayList<>(20);
                modOptions.add(353873237L);
                modOptions.add(353875897L);
                modOptions.add(353878703L);
                modOptions.add(353882671L);
                modOptions.add(353885303L);
                modOptions.add(353888377L);
                modOptions.add(353893457L);
                P = modOptions.get(new Random().nextInt(modOptions.size()));

                modOptions.clear();
                modOptions.add(452940277L);
                modOptions.add(452947687L);
                modOptions.add(464478431L);
                modOptions.add(468098221L);
                modOptions.add(470374601L);
                modOptions.add(472879717L);
                modOptions.add(472881973L);
                M = modOptions.get(new Random().nextInt(modOptions.size()));

            }
        }

        int editDistance(String a, String b) {
            a = "#"+a;
            b = "#"+b;
            int n = a.length();
            int m = b.length();
            int[][] dp = new int[n+1][m+1];
            for (int y=0; y<=n; y++) {
                for (int x=0; x<=m; x++) {
                    if (y == 0) dp[y][x] = x;
                    else if (x == 0) dp[y][x] = y;
                    else {
                        int e1 = dp[y-1][x] + 1;
                        int e2 = dp[y][x-1] + 1;
                        int e3 = dp[y-1][x-1] + (a.charAt(y-1) != b.charAt(x-1) ? 1 : 0);
                        dp[y][x] = min(e1, e2, e3);
                    }
                }
            }
            return dp[n][m];
        }

        /*************************** Technical ***************************/

        private class IO extends PrintWriter {
            private InputStreamReader r;
            private static final int BUFSIZE = 1 << 15;
            private char[] buf;
            private int bufc;
            private int bufi;
            private StringBuilder sb;

            public IO() {
                super(new BufferedOutputStream(System.out));
                r = new InputStreamReader(System.in);

                buf = new char[BUFSIZE];
                bufc = 0;
                bufi = 0;
                sb = new StringBuilder();
            }

            /** Print, flush, return nextInt. */
            private int queryInt(String s) {
                io.println(s);
                io.flush();
                return nextInt();
            }

            /** Print, flush, return nextLong. */
            private long queryLong(String s) {
                io.println(s);
                io.flush();
                return nextLong();
            }

            /** Print, flush, return next word. */
            private String queryNext(String s) {
                io.println(s);
                io.flush();
                return next();
            }

            private void fillBuf() throws IOException {
                bufi = 0;
                bufc = 0;
                while(bufc == 0) {
                    bufc = r.read(buf, 0, BUFSIZE);
                    if(bufc == -1) {
                        bufc = 0;
                        return;
                    }
                }
            }

            private boolean pumpBuf() throws IOException {
                if(bufi == bufc) {
                    fillBuf();
                }
                return bufc != 0;
            }

            private boolean isDelimiter(char c) {
                return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f';
            }

            private void eatDelimiters() throws IOException {
                while(true) {
                    if(bufi == bufc) {
                        fillBuf();
                        if(bufc == 0) throw new RuntimeException("IO: Out of input.");
                    }

                    if(!isDelimiter(buf[bufi])) break;
                    ++bufi;
                }
            }

            public String next() {
                try {
                    sb.setLength(0);

                    eatDelimiters();
                    int start = bufi;

                    while(true) {
                        if(bufi == bufc) {
                            sb.append(buf, start, bufi - start);
                            fillBuf();
                            start = 0;
                            if(bufc == 0) break;
                        }

                        if(isDelimiter(buf[bufi])) break;
                        ++bufi;
                    }

                    sb.append(buf, start, bufi - start);

                    return sb.toString();
                } catch(IOException e) {
                    throw new RuntimeException("IO.next: Caught IOException.");
                }
            }

            public int nextInt() {
                try {
                    int ret = 0;

                    eatDelimiters();

                    boolean positive = true;
                    if(buf[bufi] == '-') {
                        ++bufi;
                        if(!pumpBuf()) throw new RuntimeException("IO.nextInt: Invalid int.");
                        positive = false;
                    }

                    boolean first = true;
                    while(true) {
                        if(!pumpBuf()) break;
                        if(isDelimiter(buf[bufi])) {
                            if(first) throw new RuntimeException("IO.nextInt: Invalid int.");
                            break;
                        }
                        first = false;

                        if(buf[bufi] >= '0' && buf[bufi] <= '9') {
                            if(ret < -214748364) throw new RuntimeException("IO.nextInt: Invalid int.");
                            ret *= 10;
                            ret -= (int)(buf[bufi] - '0');
                            if(ret > 0) throw new RuntimeException("IO.nextInt: Invalid int.");
                        } else {
                            throw new RuntimeException("IO.nextInt: Invalid int.");
                        }

                        ++bufi;
                    }

                    if(positive) {
                        if(ret == -2147483648) throw new RuntimeException("IO.nextInt: Invalid int.");
                        ret = -ret;
                    }

                    return ret;
                } catch(IOException e) {
                    throw new RuntimeException("IO.nextInt: Caught IOException.");
                }
            }

            public long nextLong() {
                try {
                    long ret = 0;

                    eatDelimiters();

                    boolean positive = true;
                    if(buf[bufi] == '-') {
                        ++bufi;
                        if(!pumpBuf()) throw new RuntimeException("IO.nextLong: Invalid long.");
                        positive = false;
                    }

                    boolean first = true;
                    while(true) {
                        if(!pumpBuf()) break;
                        if(isDelimiter(buf[bufi])) {
                            if(first) throw new RuntimeException("IO.nextLong: Invalid long.");
                            break;
                        }
                        first = false;

                        if(buf[bufi] >= '0' && buf[bufi] <= '9') {
                            if(ret < -922337203685477580L) throw new RuntimeException("IO.nextLong: Invalid long.");
                            ret *= 10;
                            ret -= (long)(buf[bufi] - '0');
                            if(ret > 0) throw new RuntimeException("IO.nextLong: Invalid long.");
                        } else {
                            throw new RuntimeException("IO.nextLong: Invalid long.");
                        }

                        ++bufi;
                    }

                    if(positive) {
                        if(ret == -9223372036854775808L) throw new RuntimeException("IO.nextLong: Invalid long.");
                        ret = -ret;
                    }

                    return ret;
                } catch(IOException e) {
                    throw new RuntimeException("IO.nextLong: Caught IOException.");
                }
            }

            public double nextDouble() {
                return Double.parseDouble(next());
            }

        }

        void print(Object output) {
            io.println(output);
        }

        void done(Object output) {
            print(output);
            done();
        }

        void done() {
            io.close();
            throw new RuntimeException("Clean exit");
        }

        long min(long... v) {
            long ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.min(ans, v[i]);
            }
            return ans;
        }

        double min(double... v) {
            double ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.min(ans, v[i]);
            }
            return ans;
        }

        int min(int... v) {
            int ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.min(ans, v[i]);
            }
            return ans;
        }

        long max(long... v) {
            long ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.max(ans, v[i]);
            }
            return ans;
        }

        double max(double... v) {
            double ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.max(ans, v[i]);
            }
            return ans;
        }

        int max(int... v) {
            int ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.max(ans, v[i]);
            }
            return ans;
        }

    }

}

//package baobab;

import java.io.*;
import java.util.*;

public class D {

    public static void main(String[] args) {
        Solver solver = new Solver();
    }

    static class Solver {
        IO io;

        public Solver() {
            this.io = new IO();
            try {
                solve();
            } catch (RuntimeException e) {
                if (!e.getMessage().equals("Clean exit")) {
                    throw e;
                }
            } finally {
                io.close();
            }
        }

        /****************************** START READING HERE ********************************/

        void solve() {
            int n = io.nextInt();
            int[][] b = new int[n][n];
            for (int y=0; y<n; y++) {
                for (int x=0; x<n; x++) {
                    b[y][x] = io.nextInt();
                }
            }
            int end = n*n;
            int[] x = new int[end+1];
            int[] y = new int[end+1];
            for (int i=0; i<n; i++) {
                for (int j=0; j<n; j++) {
                    int v = b[i][j];
                    y[v] = i;
                    x[v] = j;
                }
            }

            State ans = null;
            State[][][][][] leastSteps = new State[n][n][n][n][3];
            Deque<State> q = new ArrayDeque<>();
            q.add(new State(y[1], x[1], y[2], x[2], ROOK, 0, 0));
            q.add(new State(y[1], x[1], y[2], x[2], KNIGHT, 0, 0));
            q.add(new State(y[1], x[1], y[2], x[2], BISHOP, 0, 0));
            while (!q.isEmpty()) {
                State s = q.pollFirst();
                if (s.currY < 0 || s.currX < 0 || s.currY >= n || s.currX >= n) continue;
                State prev = leastSteps[s.currY][s.currX][s.goalY][s.goalX][s.piece];
                if (prev != null) {
                    if (prev.steps < s.steps) continue;
                    if (prev.steps == s.steps && prev.replacements <= s.replacements) continue;
                }
                leastSteps[s.currY][s.currX][s.goalY][s.goalX][s.piece] = s;

                // intermediary goal reached?
                if (s.currX == s.goalX && s.currY == s.goalY) {

                    if (b[s.currY][s.currX] == end) {
                        // actually reached end state with fewest steps but not necessarily fewest replacements for that step size
                        if (ans == null) ans = s;
                        else if (ans.replacements >= s.replacements) ans = s;
                        continue; // stop bfs but dont break because we may find new solution with same steps but fewer replacements
                    }

                    // change state to set next goal.
                    int prevGoal = b[s.goalY][s.goalX];
                    int newGoal = prevGoal + 1;
                    int newGoalX = x[newGoal];
                    int newGoalY = y[newGoal];
                    // front of the queue!
                    q.addFirst(new State(s.goalY, s.goalX, newGoalY, newGoalX, s.piece, s.steps, s.replacements));
                    continue;
                }

                // change piece?
                q.addLast(new State(s.currY, s.currX, s.goalY, s.goalX, ROOK, s.steps+1, s.replacements+1));
                q.addLast(new State(s.currY, s.currX, s.goalY, s.goalX, BISHOP, s.steps+1, s.replacements+1));
                q.addLast(new State(s.currY, s.currX, s.goalY, s.goalX, KNIGHT, s.steps+1, s.replacements+1));

                // move current piece?
                if (s.piece == ROOK) {
                    for (int newY=0; newY<n; newY++) q.addLast(new State(newY, s.currX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    for (int newX=0; newX<n; newX++) q.addLast(new State(s.currY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                } else if (s.piece == KNIGHT) {
                    int newY = s.currY + 1;
                    int newX = s.currX + 2;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY + 1;
                    newX = s.currX - 2;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY - 1;
                    newX = s.currX - 2;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY - 1;
                    newX = s.currX + 2;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY + 2;
                    newX = s.currX + 1;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY - 2;
                    newX = s.currX + 1;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY + 2;
                    newX = s.currX - 1;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    newY = s.currY - 2;
                    newX = s.currX - 1;
                    q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));

                } else if (s.piece == BISHOP) {
                    int newY = s.currY;
                    int newX = s.currX;
                    while (true) {
                        newY += 1;
                        newX += 1;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY;
                    newX = s.currX;
                    while (true) {
                        newY += 1;
                        newX -= 1;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY;
                    newX = s.currX;
                    while (true) {
                        newY -= 1;
                        newX -= 1;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                    newY = s.currY;
                    newX = s.currX;
                    while (true) {
                        newY -= 1;
                        newX += 1;
                        if (newX >= n || newX < 0 || newY >= n || newY < 0) break;
                        q.addLast(new State(newY, newX, s.goalY, s.goalX, s.piece, s.steps+1, s.replacements));
                    }
                }
            }

            io.println(ans.steps + " " + ans.replacements);
        }

        static int ROOK = 0;
        static int KNIGHT = 1;
        static int BISHOP = 2;

        class State {
            int currY;
            int currX;
            int goalY;
            int goalX;
            int piece;
            int steps;
            int replacements;

            public State(int currY, int currX, int goalY, int goalX, int piece, int steps, int replacements) {
                this.currY = currY;
                this.currX = currX;
                this.goalY = goalY;
                this.goalX = goalX;
                this.piece = piece;
                this.steps = steps;
                this.replacements = replacements;
            }

            @Override
            public boolean equals(Object o) {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;
                State state = (State) o;
                return currY == state.currY &&
                        currX == state.currX &&
                        goalY == state.goalY &&
                        goalX == state.goalX &&
                        piece == state.piece;
            }

            @Override
            public int hashCode() {
                return Objects.hash(currY, currX, goalY, goalX, piece);
            }
        }

        /************************** UTILITY CODE BELOW THIS LINE **************************/

        long MOD = (long)1e9 + 7;

        boolean closeToZero(double v) {
            // Check if double is close to zero, considering precision issues.
            return Math.abs(v) <= 0.0000000001;
        }

        class DrawGrid {

            void draw(boolean[][] d) {
                System.out.print("  ");
                for (int x=0; x<d[0].length; x++) {
                    System.out.print(" " + x + " ");
                }
                System.out.println("");
                for (int y=0; y<d.length; y++) {
                    System.out.print(y + " ");
                    for (int x=0; x<d[0].length; x++) {
                        System.out.print((d[y][x] ? "[x]" : "[ ]"));
                    }
                    System.out.println("");
                }
            }

            void draw(int[][] d) {
                int max = 1;
                for (int y=0; y<d.length; y++) {
                    for (int x=0; x<d[0].length; x++) {
                        max = Math.max(max, ("" + d[y][x]).length());
                    }
                }
                System.out.print("  ");
                String format = "%" + (max+2) + "s";
                for (int x=0; x<d[0].length; x++) {
                    System.out.print(String.format(format, x) + " ");
                }
                format = "%" + (max) + "s";
                System.out.println("");
                for (int y=0; y<d.length; y++) {
                    System.out.print(y + " ");
                    for (int x=0; x<d[0].length; x++) {
                        System.out.print(" [" + String.format(format, (d[y][x])) + "]");
                    }
                    System.out.println("");
                }
            }

        }

        class IDval implements Comparable<IDval> {
            int steps;
            long replacements;

            public IDval(int steps, long replacements) {
                this.replacements = replacements;
                this.steps = steps;
            }

            @Override
            public int compareTo(IDval o) {
                if (this.replacements < o.replacements) return -1;
                if (this.replacements > o.replacements) return 1;
                return this.steps - o.steps;
            }
        }

        private class ElementCounter {
            private HashMap<Long, Integer> elements;

            public ElementCounter() {
                elements = new HashMap<>();
            }

            public void add(long element) {
                int count = 1;
                Integer prev = elements.get(element);
                if (prev != null) count += prev;
                elements.put(element, count);
            }

            public void remove(long element) {
                int count = elements.remove(element);
                count--;
                if (count > 0) elements.put(element, count);
            }

            public int get(long element) {
                Integer val = elements.get(element);
                if (val == null) return 0;
                return val;
            }

            public int size() {
                return elements.size();
            }
        }

        class StringCounter {
            HashMap<String, Integer> elements;

            public StringCounter() {
                elements = new HashMap<>();
            }

            public void add(String identifier) {
                int count = 1;
                Integer prev = elements.get(identifier);
                if (prev != null) count += prev;
                elements.put(identifier, count);
            }

            public void remove(String identifier) {
                int count = elements.remove(identifier);
                count--;
                if (count > 0) elements.put(identifier, count);
            }

            public long get(String identifier) {
                Integer val = elements.get(identifier);
                if (val == null) return 0;
                return val;
            }

            public int size() {
                return elements.size();
            }
        }

        class DisjointSet {
            /** Union Find / Disjoint Set data structure. */
            int[] size;
            int[] parent;
            int componentCount;

            public DisjointSet(int n) {
                componentCount = n;
                size = new int[n];
                parent = new int[n];
                for (int i=0; i<n; i++) parent[i] = i;
                for (int i=0; i<n; i++) size[i] = 1;
            }

            public void join(int a, int b) {
                /* Find roots */
                int rootA = parent[a];
                int rootB = parent[b];
                while (rootA != parent[rootA]) rootA = parent[rootA];
                while (rootB != parent[rootB]) rootB = parent[rootB];

                if (rootA == rootB) {
                    /* Already in the same set */
                    return;
                }

                /* Merge smaller set into larger set. */
                if (size[rootA] > size[rootB]) {
                    size[rootA] += size[rootB];
                    parent[rootB] = rootA;
                } else {
                    size[rootB] += size[rootA];
                    parent[rootA] = rootB;
                }
                componentCount--;
            }

        }

        class Trie {
            int N;
            int Z;
            int nextFreeId;
            int[][] pointers;
            boolean[] end;

            /** maxLenSum = maximum possible sum of length of words */
            public Trie(int maxLenSum, int alphabetSize) {
                this.N = maxLenSum;
                this.Z = alphabetSize;
                this.nextFreeId = 1;
                pointers = new int[N+1][alphabetSize];
                end = new boolean[N+1];
            }

            public void addWord(String word) {
                int curr = 0;
                for (int j=0; j<word.length(); j++) {
                    int c = word.charAt(j) - 'a';
                    int next = pointers[curr][c];
                    if (next == 0) {
                        next = nextFreeId++;
                        pointers[curr][c] = next;
                    }
                    curr = next;
                }
                end[curr] = true;
            }

            public boolean hasWord(String word) {
                int curr = 0;
                for (int j=0; j<word.length(); j++) {
                    int c = word.charAt(j) - 'a';
                    int next = pointers[curr][c];
                    if (next == 0) return false;
                    curr = next;
                }
                return end[curr];
            }

        }

        private static class Prob {

            /** For heavy calculations on probabilities, this class
             *  provides more accuracy & efficiency than doubles.
             *  Math explained: https://en.wikipedia.org/wiki/Log_probability
             *  Quick start:
             *      - Instantiate probabilities, eg. Prob a = new Prob(0.75)
             *      - add(), multiply() return new objects, can perform on nulls & NaNs.
             *      - get() returns probability as a readable double */

            /** Logarithmized probability. Note: 0% represented by logP NaN. */
            private double logP;

            /** Construct instance with real probability. */
            public Prob(double real) {
                if (real > 0) this.logP = Math.log(real);
                else this.logP = Double.NaN;
            }

            /** Construct instance with already logarithmized value. */
            static boolean dontLogAgain = true;
            public Prob(double logP, boolean anyBooleanHereToChooseThisConstructor) {
                this.logP = logP;
            }

            /** Returns real probability as a double. */
            public double get() {
                return Math.exp(logP);
            }

            @Override
            public String toString() {
                return ""+get();
            }

            /***************** STATIC METHODS BELOW ********************/

            /** Note: returns NaN only when a && b are both NaN/null. */
            public static Prob add(Prob a, Prob b) {
                if (nullOrNaN(a) && nullOrNaN(b)) return new Prob(Double.NaN, dontLogAgain);
                if (nullOrNaN(a)) return copy(b);
                if (nullOrNaN(b)) return copy(a);

                double x = Math.max(a.logP, b.logP);
                double y = Math.min(a.logP, b.logP);
                double sum = x + Math.log(1 + Math.exp(y - x));
                return new Prob(sum, dontLogAgain);
            }

            /** Note: multiplying by null or NaN produces NaN (repping 0% real prob). */
            public static Prob multiply(Prob a, Prob b) {
                if (nullOrNaN(a) || nullOrNaN(b)) return new Prob(Double.NaN, dontLogAgain);
                return new Prob(a.logP + b.logP, dontLogAgain);
            }

            /** Returns true if p is null or NaN. */
            private static boolean nullOrNaN(Prob p) {
                return (p == null || Double.isNaN(p.logP));
            }

            /** Returns a new instance with the same value as original. */
            private static Prob copy(Prob original) {
                return new Prob(original.logP, dontLogAgain);
            }
        }

        class Binary implements Comparable<Binary> {

            /**
             * Use example: Binary b = new Binary(Long.toBinaryString(53249834L));
             *
             * When manipulating small binary strings, instantiate new Binary(string)
             * When just reading large binary strings, instantiate new Binary(string,true)
             * get(int i) returns a character '1' or '0', not an int.
             */

            private boolean[] d;
            private int first; // Starting from left, the first (most remarkable) '1'
            public int length;

            public Binary(String binaryString) {
                this(binaryString, false);
            }
            public Binary(String binaryString, boolean initWithMinArraySize) {
                length = binaryString.length();
                int size = Math.max(2*length, 1);
                first = length/4;
                if (initWithMinArraySize) {
                    first = 0;
                    size = Math.max(length, 1);
                }
                d = new boolean[size];
                for (int i=0; i<length; i++) {
                    if (binaryString.charAt(i) == '1') d[i+first] = true;
                }
            }

            public void addFirst(char c) {
                if (first-1 < 0) doubleArraySize();
                first--;
                d[first] = (c == '1' ? true : false);
                length++;
            }

            public void addLast(char c) {
                if (first+length >= d.length) doubleArraySize();
                d[first+length] = (c == '1' ? true : false);
                length++;
            }

            private void doubleArraySize() {
                boolean[] bigArray = new boolean[(d.length+1) * 2];
                int newFirst = bigArray.length / 4;
                for (int i=0; i<length; i++) {
                    bigArray[i + newFirst] = d[i + first];
                }
                first = newFirst;
                d = bigArray;
            }

            public boolean flip(int i) {
                boolean value = (this.d[first+i] ? false : true);
                this.d[first+i] = value;
                return value;
            }

            public void set(int i, char c) {
                boolean value = (c == '1' ? true : false);
                this.d[first+i] = value;
            }

            public char get(int i) {
                return (this.d[first+i] ? '1' : '0');
            }

            @Override
            public int compareTo(Binary o) {
                if (this.length != o.length) return this.length - o.length;
                int len = this.length;
                for (int i=0; i<len; i++) {
                    int diff = this.get(i) - o.get(i);
                    if (diff != 0) return diff;
                }
                return 0;
            }

            @Override
            public String toString() {
                StringBuilder sb = new StringBuilder();
                for (int i=0; i<length; i++) {
                    sb.append(d[i+first] ? '1' : '0');
                }
                return sb.toString();
            }

        }

        /************************** Range queries **************************/

        class FenwickMin {
            long n;
            long[] original;
            long[] bottomUp;
            long[] topDown;

            public FenwickMin(int n) {
                this.n = n;
                original = new long[n+2];
                bottomUp = new long[n+2];
                topDown = new long[n+2];
            }

            public void set(int modifiedNode, long value) {
                long replaced = original[modifiedNode];
                original[modifiedNode] = value;
                // Update left tree
                int i = modifiedNode;
                long v = value;
                while (i <= n) {
                    if (v > bottomUp[i]) {
                        if (replaced == bottomUp[i]) {
                            v = Math.min(v, original[i]);
                            for (int r=1 ;; r++) {
                                int x = (i&-i)>>>r;
                                if (x == 0) break;
                                int child = i-x;
                                v = Math.min(v, bottomUp[child]);
                            }
                        } else break;
                    }
                    if (v == bottomUp[i]) break;
                    bottomUp[i] = v;
                    i += (i&-i);
                }
                // Update right tree
                i = modifiedNode;
                v = value;
                while (i > 0) {
                    if (v > topDown[i]) {
                        if (replaced == topDown[i]) {
                            v = Math.min(v, original[i]);
                            for (int r=1 ;; r++) {
                                int x = (i&-i)>>>r;
                                if (x == 0) break;
                                int child = i+x;
                                if (child > n+1) break;
                                v = Math.min(v, topDown[child]);
                            }
                        } else break;
                    }
                    if (v == topDown[i]) break;
                    topDown[i] = v;
                    i -= (i&-i);
                }
            }

            public long getMin(int a, int b) {
                long min = original[a];
                int prev = a;
                int curr = prev + (prev&-prev); // parent right hand side
                while (curr <= b) {
                    min = Math.min(min, topDown[prev]); // value from the other tree
                    prev = curr;
                    curr = prev + (prev&-prev);;
                }
                min = Math.min(min, original[prev]);
                prev = b;
                curr = prev - (prev&-prev); // parent left hand side
                while (curr >= a) {
                    min = Math.min(min,bottomUp[prev]); // value from the other tree
                    prev = curr;
                    curr = prev - (prev&-prev);
                }
                return min;
            }

        }

        class FenwickSum {
            public long[] d;

            public FenwickSum(int n) {
                d=new long[n+1];
            }

            /** a[0] must be unused. */
            public FenwickSum(long[] a) {
                d=new long[a.length];
                for (int i=1; i<a.length; i++) {
                    modify(i, a[i]);
                }
            }

            /** Do not modify i=0. */
            void modify(int i, long v) {
                while (i<d.length) {
                    d[i] += v;
                    // Move to next uplink on the RIGHT side of i
                    i += (i&-i);
                }
            }

            /** Returns sum from a to b, *BOTH* inclusive. */
            long getSum(int a, int b) {
                return getSum(b) - getSum(a-1);
            }

            private long getSum(int i) {
                long sum = 0;
                while (i>0) {
                    sum += d[i];
                    // Move to next uplink on the LEFT side of i
                    i -= (i&-i);
                }
                return sum;
            }
        }

        class SegmentTree {

            /* Provides log(n) operations for:
             * - Range query (sum, min or max)
             * - Range update ("+8 to all values between indexes 4 and 94")
             */

            int N;
            long[] lazy;
            long[] sum;
            long[] min;
            long[] max;
            boolean supportSum;
            boolean supportMin;
            boolean supportMax;

            public SegmentTree(int n) {
                this(n, true, true, true);
            }

            public SegmentTree(int n, boolean supportSum, boolean supportMin, boolean supportMax) {
                for (N=2; N<n;) N*=2;
                this.lazy = new long[2*N];
                this.supportSum = supportSum;
                this.supportMin = supportMin;
                this.supportMax = supportMax;
                if (this.supportSum) this.sum = new long[2*N];
                if (this.supportMin) this.min = new long[2*N];
                if (this.supportMax) this.max = new long[2*N];
            }

            void modifyRange(long x, int a, int b) {
                modifyRec(a, b, 1, 0, N-1, x);
            }

            void setRange() {
                //TODO
            }

            long getSum(int a, int b) {
                return querySum(a, b, 1, 0, N-1);
            }

            long getMin(int a, int b) {
                return queryMin(a, b, 1, 0, N-1);
            }

            long getMax(int a, int b) {
                return queryMax(a, b, 1, 0, N-1);
            }

            private long querySum(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return 0;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    int count = wantedRight - wantedLeft + 1;
                    return sum[i] + count * lazy[i];
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                long left = querySum(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1);
                long right = querySum(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight);
                return left + right;
            }

            private long queryMin(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return 0;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    return min[i] + lazy[i];
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                long left = queryMin(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1);
                long right = queryMin(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight);
                return min(left, right);
            }

            private long queryMax(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return 0;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    return max[i] + lazy[i];
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                long left = queryMax(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1);
                long right = queryMax(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight);
                return max(left, right);
            }

            private void modifyRec(int wantedLeft, int wantedRight, int i, int actualLeft, int actualRight, long value) {
                if (wantedLeft > actualRight || wantedRight < actualLeft) {
                    return;
                }
                if (wantedLeft == actualLeft && wantedRight == actualRight) {
                    lazy[i] += value;
                    return;
                }
                if (lazy[i] != 0) propagate(i, actualLeft, actualRight);
                int d = (actualRight - actualLeft + 1) / 2;
                modifyRec(wantedLeft, min(actualLeft+d-1, wantedRight), 2*i, actualLeft, actualLeft+d-1, value);
                modifyRec(max(actualLeft+d, wantedLeft), wantedRight, 2*i+1, actualLeft+d, actualRight, value);
                if (supportSum) sum[i] += value * (min(actualRight, wantedRight) - max(actualLeft, wantedLeft) + 1);
                if (supportMin) min[i] = min(min[2*i] + lazy[2*i], min[2*i+1] + lazy[2*i+1]);
                if (supportMax) max[i] = max(max[2*i] + lazy[2*i], max[2*i+1] + lazy[2*i+1]);
            }

            private void propagate(int i, int actualLeft, int actualRight) {
                lazy[2*i] += lazy[i];
                lazy[2*i+1] += lazy[i];
                if (supportSum) sum[i] += lazy[i] * (actualRight - actualLeft + 1);
                if (supportMin) min[i] += lazy[i];
                if (supportMax) max[i] += lazy[i];
                lazy[i] = 0;
            }

        }

        /***************************** Graphs *****************************/

        List<Integer>[] toGraph(IO io, int n) {
            List<Integer>[] g = new ArrayList[n+1];
            for (int i=1; i<=n; i++) g[i] = new ArrayList<>();
            for (int i=1; i<=n-1; i++) {
                int a = io.nextInt();
                int b = io.nextInt();
                g[a].add(b);
                g[b].add(a);
            }
            return g;
        }

        class Graph {
            HashMap<Long, List<Long>> edges;

            public Graph() {
                edges = new HashMap<>();
            }

            List<Long> getSetNeighbors(Long node) {
                List<Long> neighbors = edges.get(node);
                if (neighbors == null) {
                    neighbors = new ArrayList<>();
                    edges.put(node, neighbors);
                }
                return neighbors;
            }

            void addBiEdge(Long a, Long b) {
                addEdge(a, b);
                addEdge(b, a);
            }

            void addEdge(Long from, Long to) {
                getSetNeighbors(to); // make sure all have initialized lists
                List<Long> neighbors = getSetNeighbors(from);
                neighbors.add(to);
            }

            // topoSort variables
            int UNTOUCHED = 0;
            int FINISHED = 2;
            int INPROGRESS = 1;
            HashMap<Long, Integer> vis;
            List<Long> topoAns;
            List<Long> failDueToCycle = new ArrayList<Long>() {{ add(-1L); }};

            List<Long> topoSort() {
                topoAns = new ArrayList<>();
                vis = new HashMap<>();
                for (Long a : edges.keySet()) {
                    if (!topoDFS(a)) return failDueToCycle;
                }
                Collections.reverse(topoAns);
                return topoAns;
            }

            boolean topoDFS(long curr) {
                Integer status = vis.get(curr);
                if (status == null) status = UNTOUCHED;
                if (status == FINISHED) return true;
                if (status == INPROGRESS) {
                    return false;
                }
                vis.put(curr, INPROGRESS);
                for (long next : edges.get(curr)) {
                    if (!topoDFS(next)) return false;
                }
                vis.put(curr, FINISHED);
                topoAns.add(curr);
                return true;
            }

        }

        public class StronglyConnectedComponents {

            /** Kosaraju's algorithm */

            ArrayList<Integer>[] forw;
            ArrayList<Integer>[] bacw;

            /** Use: getCount(2, new int[] {1,2}, new int[] {2,1}) */
            public int getCount(int n, int[] mista, int[] minne) {
                forw = new ArrayList[n+1];
                bacw = new ArrayList[n+1];
                for (int i=1; i<=n; i++) {
                    forw[i] = new ArrayList<Integer>();
                    bacw[i] = new ArrayList<Integer>();
                }
                for (int i=0; i<mista.length; i++) {
                    int a = mista[i];
                    int b = minne[i];
                    forw[a].add(b);
                    bacw[b].add(a);
                }
                int count = 0;
                List<Integer> list = new ArrayList<Integer>();
                boolean[] visited = new boolean[n+1];
                for (int i=1; i<=n; i++) {
                    dfsForward(i, visited, list);
                }
                visited = new boolean[n+1];
                for (int i=n-1; i>=0; i--) {
                    int node = list.get(i);
                    if (visited[node]) continue;
                    count++;
                    dfsBackward(node, visited);
                }
                return count;
            }

            public void dfsForward(int i, boolean[] visited, List<Integer> list) {
                if (visited[i]) return;
                visited[i] = true;
                for (int neighbor : forw[i]) {
                    dfsForward(neighbor, visited, list);
                }
                list.add(i);
            }

            public void dfsBackward(int i, boolean[] visited) {
                if (visited[i]) return;
                visited[i] = true;
                for (int neighbor : bacw[i]) {
                    dfsBackward(neighbor, visited);
                }
            }
        }

        class LCAFinder {

            /* O(n log n) Initialize: new LCAFinder(graph)
             * O(log n) Queries: find(a,b) returns lowest common ancestor for nodes a and b */

            int[] nodes;
            int[] depths;
            int[] entries;
            int pointer;
            FenwickMin fenwick;

            public LCAFinder(List<Integer>[] graph) {
                this.nodes = new int[(int)10e6];
                this.depths = new int[(int)10e6];
                this.entries = new int[graph.length];
                this.pointer = 1;
                boolean[] visited = new boolean[graph.length+1];
                dfs(1, 0, graph, visited);
                fenwick = new FenwickMin(pointer-1);
                for (int i=1; i<pointer; i++) {
                    fenwick.set(i, depths[i] * 1000000L + i);
                }
            }

            private void dfs(int node, int depth, List<Integer>[] graph, boolean[] visited) {
                visited[node] = true;
                entries[node] = pointer;
                nodes[pointer] = node;
                depths[pointer] = depth;
                pointer++;
                for (int neighbor : graph[node]) {
                    if (visited[neighbor]) continue;
                    dfs(neighbor, depth+1, graph, visited);
                    nodes[pointer] = node;
                    depths[pointer] = depth;
                    pointer++;
                }
            }

            public int find(int a, int b) {
                int left = entries[a];
                int right = entries[b];
                if (left > right) {
                    int temp = left;
                    left = right;
                    right = temp;
                }
                long mixedBag = fenwick.getMin(left, right);
                int index = (int) (mixedBag % 1000000L);
                return nodes[index];
            }
        }

        /**************************** Geometry ****************************/

        class Point {
            int y;
            int x;

            public Point(int y, int x) {
                this.y = y;
                this.x = x;
            }
        }

        boolean segmentsIntersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
            // Returns true if segment 1-2 intersects segment 3-4

            if (x1 == x2 && x3 == x4) {
                // Both segments are vertical
                if (x1 != x3) return false;
                if (min(y1,y2) < min(y3,y4)) {
                    return max(y1,y2) >= min(y3,y4);
                } else {
                    return max(y3,y4) >= min(y1,y2);
                }
            }
            if (x1 == x2) {
                // Only segment 1-2 is vertical. Does segment 3-4 cross it? y = a*x + b
                double a34 = (y4-y3)/(x4-x3);
                double b34 = y3 - a34*x3;
                double y = a34 * x1 + b34;
                return y >= min(y1,y2) && y <= max(y1,y2) && x1 >= min(x3,x4) && x1 <= max(x3,x4);
            }
            if (x3 == x4) {
                // Only segment 3-4 is vertical. Does segment 1-2 cross it? y = a*x + b
                double a12 = (y2-y1)/(x2-x1);
                double b12 = y1 - a12*x1;
                double y = a12 * x3 + b12;
                return y >= min(y3,y4) && y <= max(y3,y4) && x3 >= min(x1,x2) && x3 <= max(x1,x2);
            }
            double a12 = (y2-y1)/(x2-x1);
            double b12 = y1 - a12*x1;
            double a34 = (y4-y3)/(x4-x3);
            double b34 = y3 - a34*x3;
            if (closeToZero(a12 - a34)) {
                // Parallel lines
                return closeToZero(b12 - b34);
            }
            // Non parallel non vertical lines intersect at x. Is x part of both segments?
            double x = -(b12-b34)/(a12-a34);
            return x >= min(x1,x2) && x <= max(x1,x2) && x >= min(x3,x4) && x <= max(x3,x4);
        }

        boolean pointInsideRectangle(Point p, List<Point> r, boolean countBorderAsInside) {
            Point a = r.get(0);
            Point b = r.get(1);
            Point c = r.get(2);
            Point d = r.get(3);
            double apd = areaOfTriangle(a, p, d);
            double dpc = areaOfTriangle(d, p, c);
            double cpb = areaOfTriangle(c, p, b);
            double pba = areaOfTriangle(p, b, a);
            double sumOfAreas = apd + dpc + cpb + pba;
            if (closeToZero(sumOfAreas - areaOfRectangle(r))) {
                if (closeToZero(apd) || closeToZero(dpc) || closeToZero(cpb) || closeToZero(pba)) {
                    return countBorderAsInside;
                }
                return true;
            }
            return false;
        }

        double areaOfTriangle(Point a, Point b, Point c) {
            return 0.5 * Math.abs((a.x-c.x)*(b.y-a.y)-(a.x-b.x)*(c.y-a.y));
        }

        double areaOfRectangle(List<Point> r) {
            double side1xDiff = r.get(0).x - r.get(1).x;
            double side1yDiff = r.get(0).y - r.get(1).y;
            double side2xDiff = r.get(1).x - r.get(2).x;
            double side2yDiff = r.get(1).y - r.get(2).y;
            double side1 = Math.sqrt(side1xDiff * side1xDiff + side1yDiff * side1yDiff);
            double side2 = Math.sqrt(side2xDiff * side2xDiff + side2yDiff * side2yDiff);
            return side1 * side2;
        }

        boolean pointsOnSameLine(double x1, double y1, double x2, double y2, double x3, double y3) {
            double areaTimes2 = x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2);
            return (closeToZero(areaTimes2));
        }

        class PointToLineSegmentDistanceCalculator {

            // Just call this
            double minDistFromPointToLineSegment(double point_x, double point_y, double x1, double y1, double x2, double y2) {
                return Math.sqrt(distToSegmentSquared(point_x, point_y, x1, y1, x2, y2));
            }

            private double distToSegmentSquared(double point_x, double point_y, double x1, double y1, double x2, double y2) {
                double l2 = dist2(x1,y1,x2,y2);
                if (l2 == 0) return dist2(point_x, point_y, x1, y1);
                double t = ((point_x - x1) * (x2 - x1) + (point_y - y1) * (y2 - y1)) / l2;
                if (t < 0) return dist2(point_x, point_y, x1, y1);
                if (t > 1) return dist2(point_x, point_y, x2, y2);

                double com_x = x1 + t * (x2 - x1);
                double com_y = y1 + t * (y2 - y1);
                return dist2(point_x, point_y, com_x, com_y);
            }

            private double dist2(double x1, double y1, double x2, double y2) {
                return Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2);
            }

        }

        /****************************** Math ******************************/

        long pow(long base, int exp) {
            if (exp == 0) return 1L;
            long x = pow(base, exp/2);
            long ans = x * x;
            if (exp % 2 != 0) ans *= base;
            return ans;
        }

        long gcd(long... v) {
            /** Chained calls to Euclidean algorithm. */
            if (v.length == 1) return v[0];
            long ans = gcd(v[1], v[0]);
            for (int i=2; i<v.length; i++) {
                ans = gcd(ans, v[i]);
            }
            return ans;
        }

        long gcd(long a, long b) {
            /** Euclidean algorithm. */
            if (b == 0) return a;
            return gcd(b, a%b);
        }

        int[] generatePrimesUpTo(int last) {
            /* Sieve of Eratosthenes. Practically O(n). Values of 0 indicate primes. */
            int[] div = new int[last+1];
            for (int x=2; x<=last; x++) {
                if (div[x] > 0) continue;
                for (int u=2*x; u<=last; u+=x) {
                    div[u] = x;
                }
            }
            return div;
        }

        long lcm(long a, long b) {
            /** Least common multiple */
            return a * b / gcd(a,b);
        }

        class BaseConverter {

            /* Palauttaa luvun esityksen kannassa base */
            public String convert(Long number, int base) {
                return Long.toString(number, base);
            }

            /* Palauttaa luvun esityksen kannassa baseTo, kun annetaan luku Stringinä kannassa baseFrom */
            public String convert(String number, int baseFrom, int baseTo) {
                return Long.toString(Long.parseLong(number, baseFrom), baseTo);
            }

            /* Tulkitsee kannassa base esitetyn luvun longiksi (kannassa 10) */
            public long longify(String number, int baseFrom) {
                return Long.parseLong(number, baseFrom);
            }
        }

        class BinomialCoefficients {
            /** Total number of K sized unique combinations from pool of size N (unordered)
             N! / ( K! (N - K)! )   */

            /** For simple queries where output fits in long. */
            public long biCo(long n, long k) {
                long r = 1;
                if (k > n) return 0;
                for (long d = 1; d <= k; d++) {
                    r *= n--;
                    r /= d;
                }
                return r;
            }

            /** For multiple queries with same n, different k. */
            public long[] precalcBinomialCoefficientsK(int n, int maxK) {
                long v[] = new long[maxK+1];
                v[0] = 1; // nC0 == 1
                for (int i=1; i<=n; i++) {
                    for (int j=Math.min(i,maxK); j>0; j--) {
                        v[j] = v[j] + v[j-1]; // Pascal's triangle
                    }
                }
                return v;
            }

            /** When output needs % MOD. */
            public long[] precalcBinomialCoefficientsK(int n, int k, long M) {
                long v[] = new long[k+1];
                v[0] = 1; // nC0 == 1
                for (int i=1; i<=n; i++) {
                    for (int j=Math.min(i,k); j>0; j--) {
                        v[j] = v[j] + v[j-1]; // Pascal's triangle
                        v[j] %= M;
                    }
                }
                return v;
            }
        }

        /**************************** Strings ****************************/

        class Zalgo {

            public int pisinEsiintyma(String haku, String kohde) {
                char[] s = new char[haku.length() + 1 + kohde.length()];
                for (int i=0; i<haku.length(); i++) {
                    s[i] = haku.charAt(i);
                }
                int j = haku.length();
                s[j++] = '#';
                for (int i=0; i<kohde.length(); i++) {
                    s[j++] = kohde.charAt(i);
                }
                int[] z = toZarray(s);
                int max = 0;
                for (int i=haku.length(); i<z.length; i++) {
                    max = Math.max(max, z[i]);
                }
                return max;
            }

            public int[] toZarray(char[] s) {
                int n = s.length;
                int[] z = new int[n];
                int a = 0, b = 0;
                for (int i = 1; i < n; i++) {
                    if (i > b) {
                        for (int j = i; j < n && s[j - i] == s[j]; j++)  z[i]++;
                    }
                    else {
                        z[i] = z[i - a];
                        if (i + z[i - a] > b) {
                            for (int j = b + 1; j < n && s[j - i] == s[j]; j++) z[i]++;
                            a = i;
                            b = i + z[i] - 1;
                        }
                    }
                }
                return z;
            }

            public List<Integer> getStartIndexesWhereWordIsFound(String haku, String kohde) {
                // this is alternative use case
                char[] s = new char[haku.length() + 1 + kohde.length()];
                for (int i=0; i<haku.length(); i++) {
                    s[i] = haku.charAt(i);
                }
                int j = haku.length();
                s[j++] = '#';
                for (int i=0; i<kohde.length(); i++) {
                    s[j++] = kohde.charAt(i);
                }
                int[] z = toZarray(s);
                List<Integer> indexes = new ArrayList<>();
                for (int i=haku.length(); i<z.length; i++) {
                    if (z[i] < haku.length()) continue;
                    indexes.add(i);
                }
                return indexes;
            }

        }

        class StringHasher {

            class HashedString {
                long[] hashes;
                long[] modifiers;

                public HashedString(long[] hashes, long[] modifiers) {
                    this.hashes = hashes;
                    this.modifiers = modifiers;
                }
            }

            long P;
            long M;

            public StringHasher() {
                initializePandM();
            }

            HashedString hashString(String s) {
                int n = s.length();
                long[] hashes = new long[n];
                long[] modifiers = new long[n];
                hashes[0] = s.charAt(0);
                modifiers[0] = 1;
                for (int i=1; i<n; i++) {
                    hashes[i] = (hashes[i-1] * P + s.charAt(i)) % M;
                    modifiers[i] = (modifiers[i-1] * P) % M;
                }
                return new HashedString(hashes, modifiers);
            }

            /**
             * Indices are inclusive.
             */
            long getHash(HashedString hashedString, int startIndex, int endIndex) {
                long[] hashes = hashedString.hashes;
                long[] modifiers = hashedString.modifiers;
                long result = hashes[endIndex];
                if (startIndex > 0) result -= (hashes[startIndex-1] * modifiers[endIndex-startIndex+1]) % M;
                if (result < 0) result += M;
                return result;
            }

            // Less interesting methods below

            /**
             * Efficient for 2 input parameter strings in particular.
             */
            HashedString[] hashString(String first, String second) {
                HashedString[] array = new HashedString[2];
                int n = first.length();
                long[] modifiers = new long[n];
                modifiers[0] = 1;

                long[] firstHashes = new long[n];
                firstHashes[0] = first.charAt(0);
                array[0] = new HashedString(firstHashes, modifiers);

                long[] secondHashes = new long[n];
                secondHashes[0] = second.charAt(0);
                array[1] = new HashedString(secondHashes, modifiers);

                for (int i=1; i<n; i++) {
                    modifiers[i] = (modifiers[i-1] * P) % M;
                    firstHashes[i] = (firstHashes[i-1] * P + first.charAt(i)) % M;
                    secondHashes[i] = (secondHashes[i-1] * P + second.charAt(i)) % M;
                }
                return array;
            }

            /**
             * Efficient for 3+ strings
             * More efficient than multiple hashString calls IF strings are same length.
             */
            HashedString[] hashString(String... strings) {
                HashedString[] array = new HashedString[strings.length];
                int n = strings[0].length();
                long[] modifiers = new long[n];
                modifiers[0] = 1;
                for (int j=0; j<strings.length; j++) {
                    // if all strings are not same length, defer work to another method
                    if (strings[j].length() != n) {
                        for (int i=0; i<n; i++) {
                            array[i] = hashString(strings[i]);
                        }
                        return array;
                    }

                    // otherwise initialize stuff
                    long[] hashes = new long[n];
                    hashes[0] = strings[j].charAt(0);
                    array[j] = new HashedString(hashes, modifiers);
                }
                for (int i=1; i<n; i++) {
                    modifiers[i] = (modifiers[i-1] * P) % M;
                    for (int j=0; j<strings.length; j++) {
                        String s = strings[j];
                        long[] hashes = array[j].hashes;
                        hashes[i] = (hashes[i-1] * P + s.charAt(i)) % M;
                    }
                }
                return array;
            }

            void initializePandM() {
                ArrayList<Long> modOptions = new ArrayList<>(20);
                modOptions.add(353873237L);
                modOptions.add(353875897L);
                modOptions.add(353878703L);
                modOptions.add(353882671L);
                modOptions.add(353885303L);
                modOptions.add(353888377L);
                modOptions.add(353893457L);
                P = modOptions.get(new Random().nextInt(modOptions.size()));

                modOptions.clear();
                modOptions.add(452940277L);
                modOptions.add(452947687L);
                modOptions.add(464478431L);
                modOptions.add(468098221L);
                modOptions.add(470374601L);
                modOptions.add(472879717L);
                modOptions.add(472881973L);
                M = modOptions.get(new Random().nextInt(modOptions.size()));

            }
        }

        int editDistance(String a, String b) {
            a = "#"+a;
            b = "#"+b;
            int n = a.length();
            int m = b.length();
            int[][] dp = new int[n+1][m+1];
            for (int y=0; y<=n; y++) {
                for (int x=0; x<=m; x++) {
                    if (y == 0) dp[y][x] = x;
                    else if (x == 0) dp[y][x] = y;
                    else {
                        int e1 = dp[y-1][x] + 1;
                        int e2 = dp[y][x-1] + 1;
                        int e3 = dp[y-1][x-1] + (a.charAt(y-1) != b.charAt(x-1) ? 1 : 0);
                        dp[y][x] = min(e1, e2, e3);
                    }
                }
            }
            return dp[n][m];
        }

        /*************************** Technical ***************************/

        private class IO extends PrintWriter {
            private InputStreamReader r;
            private static final int BUFSIZE = 1 << 15;
            private char[] buf;
            private int bufc;
            private int bufi;
            private StringBuilder sb;

            public IO() {
                super(new BufferedOutputStream(System.out));
                r = new InputStreamReader(System.in);

                buf = new char[BUFSIZE];
                bufc = 0;
                bufi = 0;
                sb = new StringBuilder();
            }

            /** Print, flush, return nextInt. */
            private int queryInt(String s) {
                io.println(s);
                io.flush();
                return nextInt();
            }

            /** Print, flush, return nextLong. */
            private long queryLong(String s) {
                io.println(s);
                io.flush();
                return nextLong();
            }

            /** Print, flush, return next word. */
            private String queryNext(String s) {
                io.println(s);
                io.flush();
                return next();
            }

            private void fillBuf() throws IOException {
                bufi = 0;
                bufc = 0;
                while(bufc == 0) {
                    bufc = r.read(buf, 0, BUFSIZE);
                    if(bufc == -1) {
                        bufc = 0;
                        return;
                    }
                }
            }

            private boolean pumpBuf() throws IOException {
                if(bufi == bufc) {
                    fillBuf();
                }
                return bufc != 0;
            }

            private boolean isDelimiter(char c) {
                return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f';
            }

            private void eatDelimiters() throws IOException {
                while(true) {
                    if(bufi == bufc) {
                        fillBuf();
                        if(bufc == 0) throw new RuntimeException("IO: Out of input.");
                    }

                    if(!isDelimiter(buf[bufi])) break;
                    ++bufi;
                }
            }

            public String next() {
                try {
                    sb.setLength(0);

                    eatDelimiters();
                    int start = bufi;

                    while(true) {
                        if(bufi == bufc) {
                            sb.append(buf, start, bufi - start);
                            fillBuf();
                            start = 0;
                            if(bufc == 0) break;
                        }

                        if(isDelimiter(buf[bufi])) break;
                        ++bufi;
                    }

                    sb.append(buf, start, bufi - start);

                    return sb.toString();
                } catch(IOException e) {
                    throw new RuntimeException("IO.next: Caught IOException.");
                }
            }

            public int nextInt() {
                try {
                    int ret = 0;

                    eatDelimiters();

                    boolean positive = true;
                    if(buf[bufi] == '-') {
                        ++bufi;
                        if(!pumpBuf()) throw new RuntimeException("IO.nextInt: Invalid int.");
                        positive = false;
                    }

                    boolean first = true;
                    while(true) {
                        if(!pumpBuf()) break;
                        if(isDelimiter(buf[bufi])) {
                            if(first) throw new RuntimeException("IO.nextInt: Invalid int.");
                            break;
                        }
                        first = false;

                        if(buf[bufi] >= '0' && buf[bufi] <= '9') {
                            if(ret < -214748364) throw new RuntimeException("IO.nextInt: Invalid int.");
                            ret *= 10;
                            ret -= (int)(buf[bufi] - '0');
                            if(ret > 0) throw new RuntimeException("IO.nextInt: Invalid int.");
                        } else {
                            throw new RuntimeException("IO.nextInt: Invalid int.");
                        }

                        ++bufi;
                    }

                    if(positive) {
                        if(ret == -2147483648) throw new RuntimeException("IO.nextInt: Invalid int.");
                        ret = -ret;
                    }

                    return ret;
                } catch(IOException e) {
                    throw new RuntimeException("IO.nextInt: Caught IOException.");
                }
            }

            public long nextLong() {
                try {
                    long ret = 0;

                    eatDelimiters();

                    boolean positive = true;
                    if(buf[bufi] == '-') {
                        ++bufi;
                        if(!pumpBuf()) throw new RuntimeException("IO.nextLong: Invalid long.");
                        positive = false;
                    }

                    boolean first = true;
                    while(true) {
                        if(!pumpBuf()) break;
                        if(isDelimiter(buf[bufi])) {
                            if(first) throw new RuntimeException("IO.nextLong: Invalid long.");
                            break;
                        }
                        first = false;

                        if(buf[bufi] >= '0' && buf[bufi] <= '9') {
                            if(ret < -922337203685477580L) throw new RuntimeException("IO.nextLong: Invalid long.");
                            ret *= 10;
                            ret -= (long)(buf[bufi] - '0');
                            if(ret > 0) throw new RuntimeException("IO.nextLong: Invalid long.");
                        } else {
                            throw new RuntimeException("IO.nextLong: Invalid long.");
                        }

                        ++bufi;
                    }

                    if(positive) {
                        if(ret == -9223372036854775808L) throw new RuntimeException("IO.nextLong: Invalid long.");
                        ret = -ret;
                    }

                    return ret;
                } catch(IOException e) {
                    throw new RuntimeException("IO.nextLong: Caught IOException.");
                }
            }

            public double nextDouble() {
                return Double.parseDouble(next());
            }

        }

        void print(Object output) {
            io.println(output);
        }

        void done(Object output) {
            print(output);
            done();
        }

        void done() {
            io.close();
            throw new RuntimeException("Clean exit");
        }

        long min(long... v) {
            long ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.min(ans, v[i]);
            }
            return ans;
        }

        double min(double... v) {
            double ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.min(ans, v[i]);
            }
            return ans;
        }

        int min(int... v) {
            int ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.min(ans, v[i]);
            }
            return ans;
        }

        long max(long... v) {
            long ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.max(ans, v[i]);
            }
            return ans;
        }

        double max(double... v) {
            double ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.max(ans, v[i]);
            }
            return ans;
        }

        int max(int... v) {
            int ans = v[0];
            for (int i=1; i<v.length; i++) {
                ans = Math.max(ans, v[i]);
            }
            return ans;
        }

    }

}

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;

/**
 * Created by timur on 28.03.15.
 */

public class TaskD {
    boolean eof;
    BufferedReader br;
    StringTokenizer st;
    PrintWriter out;

    public static void main(String[] args) throws IOException {
        new TaskD().run();
    }

    public String nextToken() {
        while (st == null || !st.hasMoreTokens()) {
            try {
                st = new StringTokenizer(br.readLine());
            } catch (Exception e) {
                eof = true;
                return "-1";
            }
        }
        return st.nextToken();
    }

    public int nextInt() {
        return Integer.parseInt(nextToken());
    }

    public long nextLong() {
        return Long.parseLong(nextToken());
    }

    double nextDouble() {
        return Double.parseDouble(nextToken());
    }

    String nextLine() throws IOException {
        return br.readLine();
    }

    void run() throws IOException {
        InputStream input = System.in;
        PrintStream output = System.out;
        try {
            File f = new File("a.in");
            if (f.exists() && f.canRead()) {
                input = new FileInputStream(f);
                output = new PrintStream("a.out");
            }
        } catch (Throwable e) {
        }
        br = new BufferedReader(new InputStreamReader(input));
        out = new PrintWriter(output);
        solve();
        br.close();
        out.close();
    }

    long gcd(long a, long b) {
        if (b == 0)
            return a;
        else
            return gcd(b, a % b);
    }

    class Pair implements Comparable<Pair> {
        int move, change, num;

        public Pair(int move, int change, int num) {
            this.move = move;
            this.change = change;
            this.num = num;

        }

        @Override
        public int compareTo(Pair o) {
            int r = move - o.move;
            if (r == 0)
                r = this.change - o.change;
            return r;
        }

        public Pair up() {
            return new Pair(move + 1, change + 1, num);
        }
    }

    Pair min(Pair a, Pair b) {
        if (a == null)
            return b;
        if (b == null)
            return a;
        if (b.compareTo(a) < 0)
            return b;
        return a;
    }

    Pair numin(Pair a, Pair b) {
        if (a == null)
            return b;
        if (b == null)
            return a;
        if (a.num > b.num)
            return a;
        if (a.num < b.num)
            return b;
        return min(a, b);
    }

    class map {
        Pair[][] m;

        public map() {
            m = new Pair[n][n];
        }
    }

    int n, k;

    void solve() {
        n = nextInt();
        k = n * n;
        int[] x = new int[k + 1];
        int[] y = new int[k + 1];
        int u;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                u = nextInt() - 1;
                x[u] = i;
                y[u] = j;
            }
        }
        int inf = k * 2 + 5;
        map[] h = new map[inf];
        map[] s = new map[inf];
        map[] l = new map[inf];
        for (int i = 0; i < inf; i++) {
            h[i] = new map();
            s[i] = new map();
            l[i] = new map();
        }
        h[0].m[x[0]][y[0]] = new Pair(0, 0, 1);
        l[0].m[x[0]][y[0]] = new Pair(0, 0, 1);
        s[0].m[x[0]][y[0]] = new Pair(0, 0, 1);

        int[] dxh = {-2, -2, -1, 1, 2, 2, 1, -1};
        int[] dyh = {-1, 1, 2, 2, 1, -1, -2, -2};

        int[] dxs = {-1, -1, 1, 1};
        int[] dys = {-1, 1, 1, -1};

        int[] dxl = {-1, 0, 1, 0};
        int[] dyl = {0, 1, 0, -1};

        int nx, ny;
        Pair np;
        Pair cp;

        Pair ans = null;
        for (int i = 0; i < inf - 3; i++) {
            for (int xx = 0; xx < n; xx++) {
                for (int yy = 0; yy < n; yy++) {
                    cp = h[i].m[xx][yy];
                    if (cp != null) {
                        for (int j = 0; j < 8; j++) {
                            nx = xx + dxh[j];
                            ny = yy + dyh[j];
                            if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                                np = new Pair(cp.move + 1, cp.change, cp.num);
                                if (cp.num < k && nx == x[cp.num] && ny == y[cp.num]) {
                                    np = new Pair(cp.move + 1, cp.change, cp.num + 1);
                                }
                                h[i + 1].m[nx][ny] = numin(h[i + 1].m[nx][ny], np);
                                s[i + 2].m[nx][ny] = numin(s[i + 1].m[nx][ny], np.up());
                                l[i + 2].m[nx][ny] = numin(l[i + 1].m[nx][ny], np.up());
                                ans = numin(ans, np);
                            }
                        }
                    }
                    cp = s[i].m[xx][yy];
                    if (cp != null) {
                        for (int j = 0; j < 4; j++) {
                            for (int mul = 1; mul < n + 1; mul++) {
                                nx = xx + dxs[j] * mul;
                                ny = yy + dys[j] * mul;
                                if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                                    np = new Pair(cp.move + 1, cp.change, cp.num);
                                    if (cp.num < k && nx == x[cp.num] && ny == y[cp.num]) {
                                        np = new Pair(cp.move + 1, cp.change, cp.num + 1);
                                    }
                                    s[i + 1].m[nx][ny] = numin(s[i + 1].m[nx][ny], np);
                                    h[i + 2].m[nx][ny] = numin(h[i + 1].m[nx][ny], np.up());
                                    l[i + 2].m[nx][ny] = numin(l[i + 1].m[nx][ny], np.up());
                                    ans = numin(ans, np);
                                } else {
                                    break;
                                }
                            }
                        }
                    }
                    cp = l[i].m[xx][yy];
                    if (cp != null) {
                        for (int j = 0; j < 4; j++) {
                            for (int mul = 1; mul < n + 1; mul++) {
                                nx = xx + dxl[j] * mul;
                                ny = yy + dyl[j] * mul;
                                if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                                    np = new Pair(cp.move + 1, cp.change, cp.num);
                                    if (cp.num < k && nx == x[cp.num] && ny == y[cp.num]) {
                                        np = new Pair(cp.move + 1, cp.change, cp.num + 1);
                                    }
                                    l[i + 1].m[nx][ny] = numin(l[i + 1].m[nx][ny], np);
                                    s[i + 2].m[nx][ny] = numin(s[i + 1].m[nx][ny], np.up());
                                    h[i + 2].m[nx][ny] = numin(h[i + 1].m[nx][ny], np.up());
                                    ans = numin(ans, np);
                                } else {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        out.print(ans.move + " " + ans.change);
        /*

        for (int i = 1; i < k; i++) {
            if (x[i] == x[i - 1] || y[i] == y[i - 1]) {
                l[i] = l[i - 1] + 1;
            } else {
                l[i] = l[i - 1] + 2;
            }
            zl[i] = zl[i - 1];
            s[i] = l[i] + 1;
            zs[i] = zl[i - 1] + 1;
            h[i] = l[i] + 1;
            zh[i] = zl[i - 1] + 1;
            if ((x[i] + y[i]) % 2 == (x[i - 1] + y[i - 1]) % 2) {
                if ((x[i] - y[i] == x[i - 1] - y[i - 1]) ||
                    (x[i] + y[i] == x[i - 1] + y[i - 1])) {
                    s[i] = s[i - 1] + 1;
                } else {
                    s[i] = s[i - 1] + 2;
                    if (s[i] + 1 < l[i] || (s[i] + 1 == l[i] && zs[i] + 1 < zl[i])) {
                        l[i] = s[i] + 1;
                        zl[i] = zs[i] + 1;
                    }
                    if (s[i] + 1 < h[i] || (s[i] + 1 == h[i] && zs[i] + 1 < zh[i])) {
                        h[i] = s[i] + 1;
                        zh[i] = zs[i] + 1;
                    }
                }
            }
            int mv = inf;
            Queue<Integer> xx = new ArrayDeque<>();
            Queue<Integer> yy = new ArrayDeque<>();
            Queue<Integer> c = new ArrayDeque<>();
            xx.add(x[i - 1]);
            yy.add(y[i - 1]);
            c.add(0);
            boolean[][] vis = new boolean[n][n];
            vis[x[i - 1]][y[i - 1]] = true;
            while (!xx.isEmpty()) {
                cx = xx.poll();
                cy = yy.poll();
                cc = c.poll();
                for (int j = 0; j < 8; j++) {
                    nx = cx + dx[j];
                    ny = cy + dy[j];
                    if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                        if (nx == x[i] && ny == y[i]) {
                            mv = cc + 1;
                            xx.clear();
                            break;
                        }
                        if (!vis[nx][ny]) {
                            xx.add(nx);
                            yy.add(ny);
                            c.add(cc + 1);
                            vis[nx][ny] = true;
                        }
                    }
                }
            }
            if (h[i - 1] + mv < h[i] || (h[i - 1] + mv == h[i] && zh[i] > zh[i - 1])) {
                h[i] = h[i - 1] + mv;
                zh[i] = zh[ i - 1];
            }
            if (h[i] + 1 < l[i] || (h[i] + 1 == l[i] && zh[i] + 1 < zl[i])) {
                l[i] = h[i] + 1;
                zl[i] = zh[i] + 1;
            }
            if (h[i] + 1 < s[i] || (h[i] + 1 == s[i] && zh[i] + 1 < zs[i])) {
                s[i] = h[i] + 1;
                zs[i] = zh[i] + 1;
            }
        }
        Pair lll = new Pair(l[k - 1], zl[k - 1]);
        Pair hhh = new Pair(h[k - 1], zh[k - 1]);
        Pair sss = new Pair(s[k - 1], zs[k - 1]);
        Pair res = lll;
        if (res.compareTo(hhh) > 0) {
            res = hhh;
        }
        if (res.compareTo(sss) > 0) {
            res = sss;
        }
        out.print(res.l + " " + res.change);
        */
    }

}

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Queue;
import java.util.StringTokenizer;

/**
 * Created by timur on 28.03.15.
 */

public class TaskD {
    boolean eof;
    BufferedReader br;
    StringTokenizer st;
    PrintWriter out;

    public static void main(String[] args) throws IOException {
        new TaskD().run();
    }

    public String nextToken() {
        while (st == null || !st.hasMoreTokens()) {
            try {
                st = new StringTokenizer(br.readLine());
            } catch (Exception e) {
                eof = true;
                return "-1";
            }
        }
        return st.nextToken();
    }

    public int nextInt() {
        return Integer.parseInt(nextToken());
    }

    public long nextLong() {
        return Long.parseLong(nextToken());
    }

    double nextDouble() {
        return Double.parseDouble(nextToken());
    }

    String nextLine() throws IOException {
        return br.readLine();
    }

    void run() throws IOException {
        InputStream input = System.in;
        PrintStream output = System.out;
        try {
            File f = new File("a.in");
            if (f.exists() && f.canRead()) {
                input = new FileInputStream(f);
                output = new PrintStream("a.out");
            }
        } catch (Throwable e) {
        }
        br = new BufferedReader(new InputStreamReader(input));
        out = new PrintWriter(output);
        solve();
        br.close();
        out.close();
    }

    long gcd(long a, long b) {
        if (b == 0)
            return a;
        else
            return gcd(b, a % b);
    }

    class Pair implements Comparable<Pair> {
        int l, r;

        public Pair(int l, int r) {
            this.l = l;
            this.r = r;
        }

        @Override
        public int compareTo(Pair o) {
            int r = l - o.l;
            if (r == 0)
                r = this.r - o.r;
            return r;
        }
    }

    void solve() {
        int n = nextInt();
        int k = n * n;
        int[] x = new int[k];
        int[] y = new int[k];
        int u;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                u = nextInt() - 1;
                x[u] = i;
                y[u] = j;
            }
        }
        int[] h = new int[k],
            s = new int[k],
            l = new int[k];
        int[] zh = new int[k],
            zs = new int[k],
            zl = new int[k];
        int inf = k * 2;
        for (int i = 0; i < k; i++) {
            h[i] = inf;
            s[i] = inf;
            l[i] = inf;
        }
        h[0] = 0;
        s[0] = 0;
        l[0] = 0;
        int[] dx = {-2, -2, -1, 1, 2, 2, 1, -1};
        int[] dy = {-1, 1, 2, 2, 1, -1, -2, -2};
        int cx, cy, cc, nx, ny;
        for (int i = 1; i < k; i++) {
            if (x[i] == x[i - 1] || y[i] == y[i - 1]) {
                l[i] = l[i - 1] + 1;
            } else {
                l[i] = l[i - 1] + 2;
            }
            zl[i] = zl[i - 1];
            s[i] = l[i] + 1;
            zs[i] = zl[i - 1] + 1;
            h[i] = l[i] + 1;
            zh[i] = zl[i - 1] + 1;
            if ((x[i] + y[i]) % 2 == (x[i - 1] + y[i - 1]) % 2) {
                if ((x[i] - y[i] == x[i - 1] - y[i - 1]) ||
                    (x[i] + y[i] == x[i - 1] + y[i - 1])) {
                    s[i] = s[i - 1] + 1;
                } else {
                    s[i] = s[i - 1] + 2;
                    if (s[i] + 1 < l[i] || (s[i] + 1 == l[i] && zs[i] + 1 < zl[i])) {
                        l[i] = s[i] + 1;
                        zl[i] = zs[i] + 1;
                    }
                    if (s[i] + 1 < h[i] || (s[i] + 1 == h[i] && zs[i] + 1 < zh[i])) {
                        h[i] = s[i] + 1;
                        zh[i] = zs[i] + 1;
                    }
                }
            }
            int mv = inf;
            Queue<Integer> xx = new ArrayDeque<>();
            Queue<Integer> yy = new ArrayDeque<>();
            Queue<Integer> c = new ArrayDeque<>();
            xx.add(x[i - 1]);
            yy.add(y[i - 1]);
            c.add(0);
            boolean[][] vis = new boolean[n][n];
            vis[x[i - 1]][y[i - 1]] = true;
            while (!xx.isEmpty()) {
                cx = xx.poll();
                cy = yy.poll();
                cc = c.poll();
                for (int j = 0; j < 8; j++) {
                    nx = cx + dx[j];
                    ny = cy + dy[j];
                    if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                        if (nx == x[i] && ny == y[i]) {
                            mv = cc + 1;
                            xx.clear();
                            break;
                        }
                        if (!vis[nx][ny]) {
                            xx.add(nx);
                            yy.add(ny);
                            c.add(cc + 1);
                            vis[nx][ny] = true;
                        }
                    }
                }
            }
            if (h[i - 1] + mv < h[i] || (h[i - 1] + mv == h[i] && zh[i] > zh[i - 1])) {
                h[i] = h[i - 1] + mv;
                zh[i] = zh[ i - 1];
            }
            if (h[i] + 1 < l[i] || (h[i] + 1 == l[i] && zh[i] + 1 < zl[i])) {
                l[i] = h[i] + 1;
                zl[i] = zh[i] + 1;
            }
            if (h[i] + 1 < s[i] || (h[i] + 1 == s[i] && zh[i] + 1 < zs[i])) {
                s[i] = h[i] + 1;
                zs[i] = zh[i] + 1;
            }
        }
        Pair lll = new Pair(l[k - 1], zl[k - 1]);
        Pair hhh = new Pair(h[k - 1], zh[k - 1]);
        Pair sss = new Pair(s[k - 1], zs[k - 1]);
        Pair res = lll;
        if (res.compareTo(hhh) > 0) {
            res = hhh;
        }
        if (res.compareTo(sss) > 0) {
            res = sss;
        }
        out.print(res.l + " " + res.r);
    }

}

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;

/**
 * Created by timur on 28.03.15.
 */

public class TaskD {
    boolean eof;
    BufferedReader br;
    StringTokenizer st;
    PrintWriter out;

    public static void main(String[] args) throws IOException {
        new TaskD().run();
    }

    public String nextToken() {
        while (st == null || !st.hasMoreTokens()) {
            try {
                st = new StringTokenizer(br.readLine());
            } catch (Exception e) {
                eof = true;
                return "-1";
            }
        }
        return st.nextToken();
    }

    public int nextInt() {
        return Integer.parseInt(nextToken());
    }

    public long nextLong() {
        return Long.parseLong(nextToken());
    }

    double nextDouble() {
        return Double.parseDouble(nextToken());
    }

    String nextLine() throws IOException {
        return br.readLine();
    }

    void run() throws IOException {
        InputStream input = System.in;
        PrintStream output = System.out;
        try {
            File f = new File("a.in");
            if (f.exists() && f.canRead()) {
                input = new FileInputStream(f);
                output = new PrintStream("a.out");
            }
        } catch (Throwable e) {
        }
        br = new BufferedReader(new InputStreamReader(input));
        out = new PrintWriter(output);
        solve();
        br.close();
        out.close();
    }

    long gcd(long a, long b) {
        if (b == 0)
            return a;
        else
            return gcd(b, a % b);
    }

    class Pair implements Comparable<Pair> {
        int move, change, num;

        public Pair(int move, int change, int num) {
            this.move = move;
            this.change = change;
            this.num = num;

        }

        @Override
        public int compareTo(Pair o) {
            int r = move - o.move;
            if (r == 0)
                r = this.change - o.change;
            return r;
        }

        public Pair up() {
            return new Pair(move, change + 1, num);
        }
    }

    Pair min(Pair a, Pair b) {
        if (a == null)
            return b;
        if (b == null)
            return a;
        if (b.compareTo(a) < 0)
            return b;
        return a;
    }

    Pair numin(Pair a, Pair b) {
        if (a == null)
            return b;
        if (b == null)
            return a;
        if (a.num > b.num)
            return a;
        if (a.num < b.num)
            return b;
        return min(a, b);
    }

    class map {
        Pair[][] m;

        public map() {
            m = new Pair[n][n];
        }
    }

    int n, k;

    void solve() {
        n = nextInt();
        k = n * n;
        int[] x = new int[k + 1];
        int[] y = new int[k + 1];
        int u;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                u = nextInt() - 1;
                x[u] = i;
                y[u] = j;
            }
        }
        int inf = k * 2 + 5;
        map[] h = new map[inf];
        map[] s = new map[inf];
        map[] l = new map[inf];
        for (int i = 0; i < inf; i++) {
            h[i] = new map();
            s[i] = new map();
            l[i] = new map();
        }
        h[0].m[x[0]][y[0]] = new Pair(0, 0, 0);
        l[0].m[x[0]][y[0]] = new Pair(0, 0, 0);
        s[0].m[x[0]][y[0]] = new Pair(0, 0, 0);

        int[] dxh = {-2, -2, -1, 1, 2, 2, 1, -1};
        int[] dyh = {-1, 1, 2, 2, 1, -1, -2, -2};

        int[] dxs = {-1, -1, 1, 1};
        int[] dys = {-1, 1, 1, -1};

        int[] dxl = {-1, 0, 1, 0};
        int[] dyl = {0, 1, 0, -1};

        int nx, ny;
        Pair np;
        Pair cp;

        Pair ans = null;
        for (int i = 0; i < inf - 5; i++) {
            for (int xx = 0; xx < n; xx++) {
                for (int yy = 0; yy < n; yy++) {
                    cp = h[i].m[xx][yy];
                    if (cp != null) {
                        for (int j = 0; j < 8; j++) {
                            nx = xx + dxh[j];
                            ny = yy + dyh[j];
                            if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                                np = new Pair(cp.move + 1, cp.change, cp.num);
                                if (cp.num < k - 1 && nx == x[cp.num] && ny == y[cp.num]) {
                                    np = new Pair(cp.move + 1, cp.change, cp.num + 1);
                                }
                                h[i + 1].m[nx][ny] = numin(h[i + 1].m[nx][ny], np);
                                s[i + 1].m[nx][ny] = numin(s[i + 1].m[nx][ny], np.up());
                                l[i + 1].m[nx][ny] = numin(l[i + 1].m[nx][ny], np.up());
                                ans = numin(ans, np);
                            }
                        }
                    }
                    cp = s[i].m[xx][yy];
                    if (cp != null) {
                        for (int j = 0; j < 4; j++) {
                            for (int mul = 1; mul < n + 1; mul++) {
                                nx = xx + dxs[j] * mul;
                                ny = yy + dys[j] * mul;
                                if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                                    np = new Pair(cp.move + mul, cp.change, cp.num);
                                    if (cp.num < k - 1 && nx == x[cp.num] && ny == y[cp.num]) {
                                        np = new Pair(cp.move + mul, cp.change, cp.num + 1);
                                    }
                                    s[i + 1].m[nx][ny] = numin(s[i + 1].m[nx][ny], np);
                                    h[i + 1].m[nx][ny] = numin(h[i + 1].m[nx][ny], np.up());
                                    l[i + 1].m[nx][ny] = numin(l[i + 1].m[nx][ny], np.up());
                                    ans = numin(ans, np);
                                } else {
                                    break;
                                }
                            }
                        }
                    }
                    cp = l[i].m[xx][yy];
                    if (cp != null) {
                        for (int j = 0; j < 4; j++) {
                            for (int mul = 1; mul < n + 1; mul++) {
                                nx = xx + dxl[j] * mul;
                                ny = yy + dyl[j] * mul;
                                if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                                    np = new Pair(cp.move + mul, cp.change, cp.num);
                                    if (cp.num < k - 1 && nx == x[cp.num] && ny == y[cp.num]) {
                                        np = new Pair(cp.move + mul, cp.change, cp.num + 1);
                                    }
                                    l[i + 1].m[nx][ny] = numin(l[i + 1].m[nx][ny], np);
                                    s[i + 1].m[nx][ny] = numin(s[i + 1].m[nx][ny], np.up());
                                    h[i + 1].m[nx][ny] = numin(h[i + 1].m[nx][ny], np.up());
                                    ans = numin(ans, np);
                                } else {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        out.print(ans.move + " " + (ans.change - 1));// + " " + ans.num);
        /*

        for (int i = 1; i < k; i++) {
            if (x[i] == x[i - 1] || y[i] == y[i - 1]) {
                l[i] = l[i - 1] + 1;
            } else {
                l[i] = l[i - 1] + 2;
            }
            zl[i] = zl[i - 1];
            s[i] = l[i] + 1;
            zs[i] = zl[i - 1] + 1;
            h[i] = l[i] + 1;
            zh[i] = zl[i - 1] + 1;
            if ((x[i] + y[i]) % 2 == (x[i - 1] + y[i - 1]) % 2) {
                if ((x[i] - y[i] == x[i - 1] - y[i - 1]) ||
                    (x[i] + y[i] == x[i - 1] + y[i - 1])) {
                    s[i] = s[i - 1] + 1;
                } else {
                    s[i] = s[i - 1] + 2;
                    if (s[i] + 1 < l[i] || (s[i] + 1 == l[i] && zs[i] + 1 < zl[i])) {
                        l[i] = s[i] + 1;
                        zl[i] = zs[i] + 1;
                    }
                    if (s[i] + 1 < h[i] || (s[i] + 1 == h[i] && zs[i] + 1 < zh[i])) {
                        h[i] = s[i] + 1;
                        zh[i] = zs[i] + 1;
                    }
                }
            }
            int mv = inf;
            Queue<Integer> xx = new ArrayDeque<>();
            Queue<Integer> yy = new ArrayDeque<>();
            Queue<Integer> c = new ArrayDeque<>();
            xx.add(x[i - 1]);
            yy.add(y[i - 1]);
            c.add(0);
            boolean[][] vis = new boolean[n][n];
            vis[x[i - 1]][y[i - 1]] = true;
            while (!xx.isEmpty()) {
                cx = xx.poll();
                cy = yy.poll();
                cc = c.poll();
                for (int j = 0; j < 8; j++) {
                    nx = cx + dx[j];
                    ny = cy + dy[j];
                    if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                        if (nx == x[i] && ny == y[i]) {
                            mv = cc + 1;
                            xx.clear();
                            break;
                        }
                        if (!vis[nx][ny]) {
                            xx.add(nx);
                            yy.add(ny);
                            c.add(cc + 1);
                            vis[nx][ny] = true;
                        }
                    }
                }
            }
            if (h[i - 1] + mv < h[i] || (h[i - 1] + mv == h[i] && zh[i] > zh[i - 1])) {
                h[i] = h[i - 1] + mv;
                zh[i] = zh[ i - 1];
            }
            if (h[i] + 1 < l[i] || (h[i] + 1 == l[i] && zh[i] + 1 < zl[i])) {
                l[i] = h[i] + 1;
                zl[i] = zh[i] + 1;
            }
            if (h[i] + 1 < s[i] || (h[i] + 1 == s[i] && zh[i] + 1 < zs[i])) {
                s[i] = h[i] + 1;
                zs[i] = zh[i] + 1;
            }
        }
        Pair lll = new Pair(l[k - 1], zl[k - 1]);
        Pair hhh = new Pair(h[k - 1], zh[k - 1]);
        Pair sss = new Pair(s[k - 1], zs[k - 1]);
        Pair res = lll;
        if (res.compareTo(hhh) > 0) {
            res = hhh;
        }
        if (res.compareTo(sss) > 0) {
            res = sss;
        }
        out.print(res.l + " " + res.change);
        */
    }

}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;

/* spar5h */

public class cf2{

	static int[][] dp, dp2, d, d2;
	static int[] ai, aj;

	static void update(int s_i, int f_i, int s_j, int f_j, int val, int c_i, int c_j) {

		for(int i = s_i; i <= f_i; i++) {

			for(int j = s_j; j <= f_j; j++) {

				int rpl = 0;

				if(i != c_i)
					rpl++;

				if(j != c_j)
					rpl++;

				if(d[i][j] > val + rpl) {
					d[i][j] = val + rpl; d2[i][j] = rpl;
				}
				else if(d[i][j] == val + rpl)
					d2[i][j] = min(rpl, d2[i][j]);

			}
		}
	}

	static boolean rook(int si, int sj, int ti, int tj) {

		if(si == ti || sj == tj)
			return true;

		return false;
	}

	static boolean bishop(int si, int sj, int ti, int tj) {

		if(abs(si - ti) == abs(sj - tj))
			return true;

		return false;
	}

	static boolean knight(int si, int sj, int ti, int tj) {

		if((abs(si - ti) == 1 && abs(sj - tj) == 2) || (abs(si - ti) == 2 && abs(sj - tj) == 1))
			return true;

		return false;
	}

	static void recur(int x, int n) {

		//rook: 0, bishop; 1, knight: 2

		if(x + 1 < n * n - 1)
			recur(x + 1, n);

		int si = ai[x], sj = aj[x], ti = ai[x + 1], tj = aj[x + 1];

		d = new int[3][3]; d2 = new int[3][3];

		for(int i = 0; i < 3; i++)
			for(int j = 0; j < 3; j++)
				d[i][j] = Integer.MAX_VALUE;

		update(0, 2, 0, 2, 2, 0, 0);

		//1
		if(rook(si, sj, ti, tj))
			update(0, 2, 0, 2, 1, 0, 0);

		if(bishop(si, sj, ti, tj))
			update(0, 2, 0, 2, 1, 1, 1);

		if(knight(si, sj, ti, tj))
			update(0, 2, 0, 2, 1, 2, 2);

		//2
		for(int xi = 0; xi < n; xi++) {

			for(int xj = 0; xj < n; xj++) {

				if(bishop(si, sj, xi, xj) && bishop(xi, xj, ti, tj))
					update(1, 2, 1, 2, 2, 1, 1);

				if(knight(si, sj, xi, xj) && knight(xi, xj, ti, tj))
					update(1, 2, 1, 2, 2, 2, 2);

				if(bishop(si, sj, xi, xj) && knight(xi, xj, ti, tj))
					update(1, 1, 2, 2, 3, 1, 2);

				if(knight(si, sj, xi, xj) && bishop(xi, xj, ti, tj))
					update(2, 2, 1, 1, 3, 2, 1);
			}
		}

		//3
		for(int xi = 0; xi < n; xi++) {

			for(int xj = 0; xj < n; xj++) {

				for(int yi = 0; yi < n; yi++) {

					for(int yj = 0; yj < n; yj++) {

						if(bishop(si, sj, xi, xj) && bishop(xi, xj, yi, yj) && bishop(yi, yj, ti, tj))
							update(1, 1, 1, 1, 3, 1, 1);

						if(knight(si, sj, xi, xj) && knight(xi, xj, yi, yj) && knight(yi, yj, ti, tj))
							update(2, 2, 2, 2, 3, 2, 2);
					}
				}
			}
		}

		for(int i = 0; i < 3; i++)
			dp[x][i] = Integer.MAX_VALUE;

		for(int i = 0; i < 3; i++) {
			for(int j = 0; j < 3; j++) {

				if(dp[x][i] > d[i][j] + dp[x + 1][j]) {
					dp[x][i] = d[i][j] + dp[x + 1][j]; dp2[x][i] = d2[i][j] + dp2[x + 1][j];
				}
				else if(dp[x][i] == d[i][j] + dp[x + 1][j])
					dp2[x][i] = min(d2[i][j] + dp2[x + 1][j], dp2[x][i]);
			}
		}
	}

	public static void main(String[] args) {

		InputReader s = new InputReader(System.in);
		PrintWriter w = new PrintWriter(System.out);

		int n = s.nextInt();

		ai = new int[n * n];
		aj = new int[n * n];

		for(int i = 0; i < n; i++) {

			for(int j = 0; j < n; j++) {

				int x = s.nextInt() - 1;
				ai[x] = i; aj[x] = j;
			}
		}

		dp = new int[n * n][3];
		dp2 = new int[n * n][3];

		recur(0, n);

		int v1 = Integer.MAX_VALUE, v2 = 0;

		for(int i = 0; i < 3; i++) {

			if(dp[0][i] < v1) {
				v1 = dp[0][i]; v2 = dp2[0][i];
			}
			else if(dp[0][i] == v1)
				v2 = min(dp2[0][i], v2);
		}

		w.println(v1 + " " + v2);

		w.close();
	}

	static class InputReader {

		private InputStream stream;
		private byte[] buf = new byte[1024];
		private int curChar;
		private int numChars;
		private SpaceCharFilter filter;

		public InputReader(InputStream stream)
		{
			this.stream = stream;
		}

		public int read()
		{
			if (numChars==-1)
				throw new InputMismatchException();

			if (curChar >= numChars)
			{
				curChar = 0;
				try
				{
					numChars = stream.read(buf);
				}
				catch (IOException e)
				{
					throw new InputMismatchException();
				}

				if(numChars <= 0)
					return -1;
			}
			return buf[curChar++];
		}

		public String nextLine()
		{
			BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
			String str = "";
            try
            {
                str = br.readLine();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            return str;
		}
		public int nextInt()
		{
			int c = read();

			while(isSpaceChar(c))
				c = read();

			int sgn = 1;

			if (c == '-')
			{
				sgn = -1;
				c = read();
			}

			int res = 0;
			do
			{
				if(c<'0'||c>'9')
					throw new InputMismatchException();
				res *= 10;
				res += c - '0';
				c = read();
			}
			while (!isSpaceChar(c));

			return res * sgn;
		}

		public long nextLong()
		{
			int c = read();
			while (isSpaceChar(c))
				c = read();
			int sgn = 1;
			if (c == '-')
			{
				sgn = -1;
				c = read();
			}
			long res = 0;

			do
			{
				if (c < '0' || c > '9')
					throw new InputMismatchException();
				res *= 10;
				res += c - '0';
				c = read();
			}
			while (!isSpaceChar(c));
				return res * sgn;
		}

		public double nextDouble()
		{
			int c = read();
			while (isSpaceChar(c))
				c = read();
			int sgn = 1;
			if (c == '-')
			{
				sgn = -1;
				c = read();
			}
			double res = 0;
			while (!isSpaceChar(c) && c != '.')
			{
				if (c == 'e' || c == 'E')
					return res * Math.pow(10, nextInt());
				if (c < '0' || c > '9')
					throw new InputMismatchException();
				res *= 10;
				res += c - '0';
				c = read();
			}
			if (c == '.')
			{
				c = read();
				double m = 1;
				while (!isSpaceChar(c))
				{
					if (c == 'e' || c == 'E')
						return res * Math.pow(10, nextInt());
					if (c < '0' || c > '9')
						throw new InputMismatchException();
					m /= 10;
					res += (c - '0') * m;
					c = read();
				}
			}
			return res * sgn;
		}

		public String readString()
		{
			int c = read();
			while (isSpaceChar(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do
			{
				res.appendCodePoint(c);
				c = read();
			}
			while (!isSpaceChar(c));

			return res.toString();
		}

		public boolean isSpaceChar(int c)
		{
			if (filter != null)
				return filter.isSpaceChar(c);
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		public String next()
		{
			return readString();
		}

		public interface SpaceCharFilter
		{
			public boolean isSpaceChar(int ch);
		}
	}

}

import java.io.*;
import java.math.*;
import java.util.*;

public class ThreePieces {
    private static final int INF = 1234567890;

    public static void solve(FastIO io) {
        int N = io.nextInt();

        int r0 = -1, c0 = -1;
        int[][] A = new int[N][];
        for (int i = 0; i < N; ++i) {
            A[i] = io.nextIntArray(N);

            for (int j = 0; j < N; ++j) {
                if (A[i][j] == 1) {
                    r0 = i;
                    c0 = j;
                }
            }
        }

        LocList mb = new LocList();
        for (int i = 1; i <= N; ++i) {
            mb.add(new Loc(i, i));
            mb.add(new Loc(i, -i));
            mb.add(new Loc(-i, i));
            mb.add(new Loc(-i, -i));
        }

        LocList mk = new LocList();
        mk.add(new Loc(-2, 1));
        mk.add(new Loc(-1, 2));
        mk.add(new Loc(1, 2));
        mk.add(new Loc(2, 1));
        mk.add(new Loc(2, -1));
        mk.add(new Loc(1, -2));
        mk.add(new Loc(-1, -2));
        mk.add(new Loc(-2, -1));

        LocList mr = new LocList();
        for (int i = 1; i <= N; ++i) {
            mr.add(new Loc(0, i));
            mr.add(new Loc(0, -1));
            mr.add(new Loc(i, 0));
            mr.add(new Loc(-i, 0));
        }

        LocList[] moves = {mb, mk, mr};

        int N2 = N * N;
        int[][][][] dp = megaInit(N);
        dp[r0][c0][1][0] = dp[r0][c0][1][1] = dp[r0][c0][1][2] = 0;

        for (int t = 0;; ++t) {
            int[][][][] next = megaInit(N);
            int bestFinish = INF;
            for (int r = 0; r < N; ++r) {
                for (int c = 0; c < N; ++c) {
                    for (int i = 1; i < N2; ++i) {
                        int ni = i;
                        if (A[r][c] == i) {
                            ++ni;
                        }
                        for (int p = 0; p < 3; ++p) {
                            for (int np = 0; np < 3; ++np) {
                                next[r][c][ni][np] = Math.min(next[r][c][ni][np], 1 + dp[r][c][i][p]);
                            }
                            if (dp[r][c][i][p] < INF) {
                                for (Loc mv : moves[p]) {
                                    int nr = r + mv.R;
                                    int nc = c + mv.C;
                                    if (nr >= 0 && nr < N && nc >= 0 && nc < N) {
                                        next[nr][nc][ni][p] = Math.min(next[nr][nc][ni][p], dp[r][c][i][p]);
                                    }
                                }
                            }
                        }
                    }
                    for (int p = 0; p < 3; ++p) {
                        bestFinish = Math.min(bestFinish, dp[r][c][N2][p]);
                    }
                }
            }
            if (bestFinish < INF) {
                io.printf("%d %d\n", t, bestFinish);
                return;
            }
            dp = next;
        }
    }

    private static int[][][][] megaInit(int N) {
        final int N2 = N * N;
        int[][][][] arr = new int[N][N][N2 + 1][3];
        for (int r = 0; r < N; ++r) {
            for (int c = 0; c < N; ++c) {
                for (int i = 0; i <= N2; ++i) {
                    Arrays.fill(arr[r][c][i], INF);
                }
            }
        }
        return arr;
    }

    private static class LocList extends ArrayList<Loc> {
    }

    private static class Loc {
        public int R, C;

        public Loc(int r, int c) {
            this.R = r;
            this.C = c;
        }
    }

    public static class FastIO {
        private InputStream reader;
        private PrintWriter writer;

        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;

        public FastIO(InputStream r, OutputStream w) {
            reader = r;
            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(w)));
        }

        public int read() {
            if (numChars == -1)
                throw new InputMismatchException();
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = reader.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (numChars <= 0)
                    return -1;
            }
            return buf[curChar++];
        }

        public String nextLine() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isEndOfLine(c));
            return res.toString();
        }

        public String nextString() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isSpaceChar(c));
            return res.toString();
        }

        public long nextLong() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            long res = 0;
            do {
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public int nextInt() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public int[] nextIntArray(int n) {
            return nextIntArray(n, 0);
        }

        public int[] nextIntArray(int n, int off) {
            int[] arr = new int[n + off];
            for (int i = 0; i < n; i++) {
                arr[i + off] = nextInt();
            }
            return arr;
        }

        public long[] nextLongArray(int n) {
            return nextLongArray(n, 0);
        }

        public long[] nextLongArray(int n, int off) {
            long[] arr = new long[n + off];
            for (int i = 0; i < n; i++) {
                arr[i + off] = nextLong();
            }
            return arr;
        }

        private boolean isSpaceChar(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        private boolean isEndOfLine(int c) {
            return c == '\n' || c == '\r' || c == -1;
        }

        public void print(Object... objects) {
            for (int i = 0; i < objects.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(objects[i]);
            }
        }

        public void println(Object... objects) {
            print(objects);
            writer.println();
        }

        public void printArray(int[] arr) {
            for (int i = 0; i < arr.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(arr[i]);
            }
        }

        public void printArray(long[] arr) {
            for (int i = 0; i < arr.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(arr[i]);
            }
        }

        public void printlnArray(int[] arr) {
            printArray(arr);
            writer.println();
        }

        public void printlnArray(long[] arr) {
            printArray(arr);
            writer.println();
        }

        public void printf(String format, Object... args) {
            print(String.format(format, args));
        }

        public void flush() {
            writer.flush();
        }
    }

    public static void main(String[] args) {
        FastIO io = new FastIO(System.in, System.out);
        solve(io);
        io.flush();
    }
}

/*
If you want to aim high, aim high
Don't let that studying and grades consume you
Just live life young
******************************
What do you think? What do you think?
1st on Billboard, what do you think of it
Next is a Grammy, what do you think of it
However you think, I’m sorry, but shit, I have no fcking interest
*******************************
I'm standing on top of my Monopoly board
That means I'm on top of my game and it don't stop
til my hip don't hop anymore
https://www.a2oj.com/Ladder16.html
*******************************
Shining through the city with a little funk and soul
*******************************
300iq as writer = Sad!
*/
import java.util.*;
import java.io.*;
import java.math.*;

   public class x1065D
   {
      public static void main(String hi[]) throws Exception
      {
         BufferedReader infile = new BufferedReader(new InputStreamReader(System.in));
         StringTokenizer st = new StringTokenizer(infile.readLine());
         int N = Integer.parseInt(st.nextToken());
         int[][] grid = new int[N][N];
         for(int i=0; i < N; i++)
         {
            st = new StringTokenizer(infile.readLine());
            for(int a=0; a < N; a++)
               grid[i][a] = Integer.parseInt(st.nextToken());
         }
         int[] loc = new int[N*N+1];
         for(int r=0; r < N; r++)
            for(int c=0; c < N; c++)
               loc[grid[r][c]] = r*N+c;
         //knight bishop rook
         int[][][] dist = new int[N*N+1][3][N*N+1];
         int[][][] replace = new int[N*N+1][3][N*N+1];
         for(int i=0; i <= N*N; i++)
            for(int p=0; p < 3; p++)
            {
               Arrays.fill(dist[i][p], 420420420);
               Arrays.fill(replace[i][p], 420420420);
            }
         int start = loc[1];
         for(int p=0; p < 3; p++)
            dist[start][p][1] = replace[start][p][1] = 0;
         ArrayDeque<Integer> q = new ArrayDeque<Integer>();
         for(int p=0; p < 1; p++)
         {
            q.add(start);
            q.add(p);
            q.add(1);
         }
         while(q.size() > 0)
         {
            int tempo = q.poll();
            int piece = q.poll();
            int target = q.poll();
            int x = tempo/N;
            int y = tempo%N;
            //swap
            for(int p=0; p < 3; p++)
            {
               if(piece == p)
                  continue;
               if(replace[tempo][p][target] > replace[tempo][piece][target]+1 && dist[tempo][p][target] == dist[tempo][piece][target]+1)
               {
                  replace[tempo][p][target] = replace[tempo][piece][target]+1;
                  q.add(tempo);
                  q.add(p);
                  q.add(target);
               }
               else if(dist[tempo][p][target] > dist[tempo][piece][target]+1)
               {
                  replace[tempo][p][target] = replace[tempo][piece][target]+1;
                  dist[tempo][p][target] = dist[tempo][piece][target]+1;
                  q.add(tempo);
                  q.add(p);
                  q.add(target);
               }
            }
            //move
            if(piece == 0)
            {
               for(int dx=-2; dx <= 2; dx++)
                  for(int dy=-2; dy <= 2; dy++)
                  {
                     if(dx == 0 || dy == 0 || Math.abs(dx) == Math.abs(dy))
                        continue;
                     int r = x+dx;
                     int c = y+dy;
                     if(Math.min(r,c) < 0 || Math.max(r,c) >= N)
                        continue;
                     int id = target+(grid[r][c]-1 == target ? 1 : 0);
                     if(dist[r*N+c][piece][id] > dist[tempo][piece][target]+1)
                     {
                        replace[r*N+c][piece][id] = replace[tempo][piece][target];
                        dist[r*N+c][piece][id] = dist[tempo][piece][target]+1;
                        q.add(r*N+c);
                        q.add(piece);
                        q.add(id);
                     }
                     else if(dist[r*N+c][piece][id] == dist[tempo][piece][target]+1 && replace[r*N+c][piece][id] > replace[tempo][piece][target])
                     {
                        replace[r*N+c][piece][id] = replace[tempo][piece][target];
                        q.add(r*N+c);
                        q.add(piece);
                        q.add(id);
                     }
                  }
            }
            else if(piece == 1)
            {
               for(int a=1; a < N; a++)
                  for(int dx=-1; dx <= 1; dx+=2)
                     for(int dy=-1; dy <= 1; dy+=2)
                     {
                        int r = x+dx*a;
                        int c = y+dy*a;
                        if(Math.min(r,c) < 0 || Math.max(r,c) >= N)
                           continue;
                        int id = target+(grid[r][c]-1 == target ? 1 : 0);
                        if(dist[r*N+c][piece][id] > dist[tempo][piece][target]+1)
                        {
                           replace[r*N+c][piece][id] = replace[tempo][piece][target];
                           dist[r*N+c][piece][id] = dist[tempo][piece][target]+1;
                           q.add(r*N+c);
                           q.add(piece);
                           q.add(id);
                        }
                        else if(dist[r*N+c][piece][id] == dist[tempo][piece][target]+1 && replace[r*N+c][piece][id] > replace[tempo][piece][target])
                        {
                           replace[r*N+c][piece][id] = replace[tempo][piece][target];
                           q.add(r*N+c);
                           q.add(piece);
                           q.add(id);
                        }
                     }
            }
            else
            {
               for(int a=1; a < N; a++)
                  for(int dx=-1; dx <= 1; dx+=2)
                  {
                     int r = x+dx*a;
                     int c = y;
                     if(Math.min(r,c) < 0 || Math.max(r,c) >= N)
                        continue;
                     int id = target+(grid[r][c]-1 == target ? 1 : 0);
                     if(dist[r*N+c][piece][id] > dist[tempo][piece][target]+1)
                     {
                        replace[r*N+c][piece][id] = replace[tempo][piece][target];
                        dist[r*N+c][piece][id] = dist[tempo][piece][target]+1;
                        q.add(r*N+c);
                        q.add(piece);
                        q.add(id);
                     }
                     else if(dist[r*N+c][piece][id] == dist[tempo][piece][target]+1 && replace[r*N+c][piece][id] > replace[tempo][piece][target])
                     {
                        replace[r*N+c][piece][id] = replace[tempo][piece][target];
                        q.add(r*N+c);
                        q.add(piece);
                        q.add(id);
                     }
                  }
               for(int a=1; a < N; a++)
                  for(int dy=-1; dy <= 1; dy+=2)
                  {
                     int r = x;
                     int c = y+dy*a;
                     if(Math.min(r,c) < 0 || Math.max(r,c) >= N)
                        continue;
                     int id = target+(grid[r][c]-1 == target ? 1 : 0);
                     if(dist[r*N+c][piece][id] > dist[tempo][piece][target]+1)
                     {
                        replace[r*N+c][piece][id] = replace[tempo][piece][target];
                        dist[r*N+c][piece][id] = dist[tempo][piece][target]+1;
                        q.add(r*N+c);
                        q.add(piece);
                        q.add(id);
                     }
                     else if(dist[r*N+c][piece][id] == dist[tempo][piece][target]+1 && replace[r*N+c][piece][id] > replace[tempo][piece][target])
                     {
                        replace[r*N+c][piece][id] = replace[tempo][piece][target];
                        q.add(r*N+c);
                        q.add(piece);
                        q.add(id);
                     }
                  }
            }
         }
         int minDist = 420420420;
         int minRep = 420420420;
         int lmao = N*N;
         for(int p=0; p < 3; p++)
         {
            if(minDist > dist[loc[lmao]][p][lmao])
            {
               minDist = dist[loc[lmao]][p][lmao];
               minRep = replace[loc[lmao]][p][lmao];
            }
            else if(minDist == dist[loc[lmao]][p][lmao] && minRep > replace[loc[lmao]][p][lmao])
               minRep = replace[loc[lmao]][p][lmao];
         }
         System.out.println(minDist+" "+minRep);
      }
   }

import java.util.*;
import java.io.*;

public class Main {
	public static void main(String args[]) {new Main().run();}

	FastReader in = new FastReader();
	PrintWriter out = new PrintWriter(System.out);
	void run(){
		work();
		out.flush();
	}
	long mod=1000000007;
	long inf=Long.MAX_VALUE;
	long gcd(long a,long b) {
		return a==0?b:gcd(b%a,a);
	}
	int n;
	int[][] dir=new int[][] {{1,2},{2,1},{-1,2},{-2,1},{1,-2},{2,-1},{-1,-2},{-2,-1}};
	void work() {
		n=ni();
		int[][] rec=new int[n*n][];
		for(int i=0;i<n;i++) {
			for(int j=0;j<n;j++) {
				rec[ni()-1]=new int[] {i,j};
			}
		}
		int[][] dp=new int[3][2];//车相马,step replacement
		for(int i=0;i<n*n-1;i++) {
			int[][] ndp=new int[3][2];
			for(int[] d:ndp) {
				Arrays.fill(d, 99999999);
			}
			for(int j=0;j<3;j++) {
				int[][] A=bfs(rec[i],rec[i+1],j);
				for(int k=0;k<3;k++) {
					int v1=dp[j][0]+A[k][0];
					int v2=dp[j][1]+A[k][1];
					if(v1<ndp[k][0]||(v1==ndp[k][1]&&v2<ndp[k][1])) {
						ndp[k][0]=v1;
						ndp[k][1]=v2;
					}
				}
			}
			dp=ndp;
		}
		int s=99999999,r=99999999;
		for(int[] d:dp) {
			if(d[0]<s) {
				s=d[0];
				r=d[1];
			}else if(d[0]==s&&d[1]<r) {
				r=d[1];
			}
		}
		out.println(s+" "+r);
	}

	private int[][] bfs(int[] start, int[] end, int state) {
		int[][][][] rec=new int[n][n][3][2];
		for(int i=0;i<n;i++) {
			for(int j=0;j<n;j++) {
				for(int k=0;k<3;k++) {
					rec[i][j][k][0]=99999999;
					rec[i][j][k][1]=99999999;
				}
			}
		}
		rec[start[0]][start[1]][state][0]=0;
		rec[start[0]][start[1]][state][1]=0;
		LinkedList<int[]> queue=new LinkedList<>();
		queue.add(new int[] {start[0],start[1],0,0,state});
		while(queue.size()>0) {
			int[] q=queue.poll();
			int x=q[0],y=q[1],step=q[2],rep=q[3],s=q[4];
			for(int i=0;i<3;i++) {
				int nstep=step+1;
				int nrep=rep+1;
				if(rec[x][y][i][0]>nstep||(rec[x][y][i][0]==nstep&&rec[x][y][i][1]>nrep)) {
					rec[x][y][i][0]=nstep;
					rec[x][y][i][1]=nrep;
					queue.add(new int[] {x,y,nstep,nrep,i});
				}
			}
			if(s==0) {
				for(int i=0;i<n;i++) {
					int nstep=step+1;
					if(rec[i][y][s][0]>nstep||(rec[i][y][s][0]==nstep&&rec[i][y][s][1]>rep)) {
						rec[i][y][s][0]=nstep;
						rec[i][y][s][1]=rep;
						queue.add(new int[] {i,y,nstep,rep,s});
					}
					if(rec[x][i][s][0]>nstep||(rec[x][i][s][0]==nstep&&rec[x][i][s][1]>rep)) {
						rec[x][i][s][0]=nstep;
						rec[x][i][s][1]=rep;
						queue.add(new int[] {x,i,nstep,rep,s});
					}
				}
			}else if(s==1) {
				for(int i=-n;i<n;i++) {
					int nstep=step+1;
					int nx=x+i,ny=y+i;
					if(nx>=0&&nx<n&&ny>=0&&ny<n) {
						if(rec[nx][ny][s][0]>nstep||(rec[nx][ny][s][0]==nstep&&rec[nx][ny][s][1]>rep)) {
							queue.add(new int[] {nx,ny,nstep,rep,s});
							rec[nx][ny][s][0]=nstep;
							rec[nx][ny][s][1]=rep;
						}
					}
					nx=x-i;ny=y+i;
					if(nx<0||nx>=n||ny<0||ny>=n)continue;
					if(rec[nx][ny][s][0]>nstep||(rec[nx][ny][s][0]==nstep&&rec[nx][ny][s][1]>rep)) {
						queue.add(new int[] {nx,ny,nstep,rep,s});
						rec[nx][ny][s][0]=nstep;
						rec[nx][ny][s][1]=rep;
					}
				}
			}else {
				for(int i=0;i<8;i++) {
					int nx=x+dir[i][0],ny=y+dir[i][1];
					int nstep=step+1;
					if(nx<0||nx>=n||ny<0||ny>=n)continue;
					if(rec[nx][ny][s][0]>nstep||(rec[nx][ny][s][0]==nstep&&rec[nx][ny][s][1]>rep)) {
						queue.add(new int[] {nx,ny,nstep,rep,s});
						rec[nx][ny][s][0]=nstep;
						rec[nx][ny][s][1]=rep;
					}
				}
			}
		}
		return rec[end[0]][end[1]];
	}

	//input
	@SuppressWarnings("unused")
	private ArrayList<Integer>[] ng(int n, int m) {
		ArrayList<Integer>[] graph=(ArrayList<Integer>[])new ArrayList[n];
		for(int i=0;i<n;i++) {
			graph[i]=new ArrayList<>();
		}
		for(int i=1;i<=m;i++) {
			int s=in.nextInt()-1,e=in.nextInt()-1;
			graph[s].add(e);
			graph[e].add(s);
		}
		return graph;
	}

	private ArrayList<long[]>[] ngw(int n, int m) {
		ArrayList<long[]>[] graph=(ArrayList<long[]>[])new ArrayList[n];
		for(int i=0;i<n;i++) {
			graph[i]=new ArrayList<>();
		}
		for(int i=1;i<=m;i++) {
			long s=in.nextLong()-1,e=in.nextLong()-1,w=in.nextLong();
			graph[(int)s].add(new long[] {e,w,i});
			graph[(int)e].add(new long[] {s,w});
		}
		return graph;
	}

	private int ni() {
		return in.nextInt();
	}

	private long nl() {
		return in.nextLong();
	}

	private String ns() {
		return in.next();
	}

	private long[] na(int n) {
		long[] A=new long[n];
		for(int i=0;i<n;i++) {
			A[i]=in.nextLong();
		}
		return A;
	}
	private int[] nia(int n) {
		int[] A=new int[n];
		for(int i=0;i<n;i++) {
			A[i]=in.nextInt();
		}
		return A;
	}
}

class FastReader
{
	BufferedReader br;
	StringTokenizer st;

	public FastReader()
	{
		br=new BufferedReader(new InputStreamReader(System.in));
	}

	public String next()
	{
		while(st==null || !st.hasMoreElements())//回车，空行情况
		{
			try {
				st = new StringTokenizer(br.readLine());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return st.nextToken();
	}

	public int nextInt()
	{
		return Integer.parseInt(next());
	}

	public long nextLong()
	{
		return Long.parseLong(next());
	}
}

import java.util.*;
import java.io.*;

public class Main {
	public static void main(String args[]) {new Main().run();}

	FastReader in = new FastReader();
	PrintWriter out = new PrintWriter(System.out);
	void run(){
		work();
		out.flush();
	}
	long mod=1000000007;
	long inf=Long.MAX_VALUE;
	long gcd(long a,long b) {
		return a==0?b:gcd(b%a,a);
	}
	int n;
	int[][] dir=new int[][] {{1,2},{2,1},{-1,2},{-2,1},{1,-2},{2,-1},{-1,-2},{-2,-1}};
	void work() {
		n=ni();
		int[][] rec=new int[n*n][];
		for(int i=0;i<n;i++) {
			for(int j=0;j<n;j++) {
				rec[ni()-1]=new int[] {i,j};
			}
		}
		int[][] dp=new int[3][2];//车相马,step replacement
		for(int i=0;i<n*n-1;i++) {
			int[][] ndp=new int[3][2];
			for(int[] d:ndp) {
				Arrays.fill(d, 99999999);
			}
			for(int j=0;j<3;j++) {
				int[][] A=bfs(rec[i],rec[i+1],j);
				for(int k=0;k<3;k++) {
					int v1=dp[j][0]+A[k][0];
					int v2=dp[j][1]+A[k][1];
					if(v1<ndp[k][0]||(v1==ndp[k][1]&&v2<ndp[k][1])) {
						ndp[k][0]=v1;
						ndp[k][1]=v2;
					}
				}
			}
			dp=ndp;
		}
		int s=99999999,r=99999999;
		for(int[] d:dp) {
			if(d[0]<s) {
				s=d[0];
				r=d[1];
			}else if(d[0]==s&&d[1]<r) {
				r=d[1];
			}
		}
		out.println(s+" "+r);
	}

	private int[][] bfs(int[] start, int[] end, int state) {
		int[][][][] rec=new int[n][n][3][2];
		for(int i=0;i<n;i++) {
			for(int j=0;j<n;j++) {
				for(int k=0;k<3;k++) {
					rec[i][j][k][0]=99999999;
					rec[i][j][k][1]=99999999;
				}
			}
		}
		rec[start[0]][start[1]][state][0]=0;
		rec[start[0]][start[1]][state][1]=0;
		LinkedList<int[]> queue=new LinkedList<>();
		queue.add(new int[] {start[0],start[1],0,0,state});
		while(queue.size()>0) {
			int[] q=queue.poll();
			int x=q[0],y=q[1],step=q[2],rep=q[3],s=q[4];
			for(int i=0;i<3;i++) {
				int nstep=step+1;
				int nrep=rep+1;
				if(rec[x][y][i][0]>nstep||(rec[x][y][i][0]==nstep&&rec[x][y][i][1]>nrep)) {
					rec[x][y][i][0]=nstep;
					rec[x][y][i][1]=nrep;
					queue.add(new int[] {x,y,nstep,nrep,i});
				}
			}
			if(s==0) {
				for(int i=0;i<n;i++) {
					int nstep=step+1;
					if(rec[i][y][s][0]>nstep||(rec[i][y][s][0]==nstep&&rec[i][y][s][1]>rep)) {
						rec[i][y][s][0]=nstep;
						rec[i][y][s][1]=rep;
						queue.add(new int[] {i,y,nstep,rep,s});
					}
					if(rec[x][i][s][0]>nstep||(rec[x][i][s][0]==nstep&&rec[x][i][s][1]>rep)) {
						rec[x][i][s][0]=nstep;
						rec[x][i][s][1]=rep;
						queue.add(new int[] {x,i,nstep,rep,s});
					}
				}
			}else if(s==1) {
				for(int i=-n;i<n;i++) {
					int nstep=step+1;
					int nx=x+i,ny=y+i;
					if(nx<0||nx>=n||ny<0||ny>=n)continue;
					if(rec[nx][ny][s][0]>nstep||(rec[nx][ny][s][0]==nstep&&rec[nx][ny][s][1]>rep)) {
						queue.add(new int[] {nx,ny,nstep,rep,s});
						rec[nx][ny][s][0]=nstep;
						rec[nx][ny][s][1]=rep;
					}
					nx=x-i;ny=y+i;
					if(nx<0||nx>=n||ny<0||ny>=n)continue;
					if(rec[nx][ny][s][0]>nstep||(rec[nx][ny][s][0]==nstep&&rec[nx][ny][s][1]>rep)) {
						queue.add(new int[] {nx,ny,nstep,rep,s});
						rec[nx][ny][s][0]=nstep;
						rec[nx][ny][s][1]=rep;
					}
				}
			}else {
				for(int i=0;i<8;i++) {
					int nx=x+dir[i][0],ny=y+dir[i][1];
					int nstep=step+1;
					if(nx<0||nx>=n||ny<0||ny>=n)continue;
					if(rec[nx][ny][s][0]>nstep||(rec[nx][ny][s][0]==nstep&&rec[nx][ny][s][1]>rep)) {
						queue.add(new int[] {nx,ny,nstep,rep,s});
						rec[nx][ny][s][0]=nstep;
						rec[nx][ny][s][1]=rep;
					}
				}
			}
		}
		return rec[end[0]][end[1]];
	}

	//input
	@SuppressWarnings("unused")
	private ArrayList<Integer>[] ng(int n, int m) {
		ArrayList<Integer>[] graph=(ArrayList<Integer>[])new ArrayList[n];
		for(int i=0;i<n;i++) {
			graph[i]=new ArrayList<>();
		}
		for(int i=1;i<=m;i++) {
			int s=in.nextInt()-1,e=in.nextInt()-1;
			graph[s].add(e);
			graph[e].add(s);
		}
		return graph;
	}

	private ArrayList<long[]>[] ngw(int n, int m) {
		ArrayList<long[]>[] graph=(ArrayList<long[]>[])new ArrayList[n];
		for(int i=0;i<n;i++) {
			graph[i]=new ArrayList<>();
		}
		for(int i=1;i<=m;i++) {
			long s=in.nextLong()-1,e=in.nextLong()-1,w=in.nextLong();
			graph[(int)s].add(new long[] {e,w,i});
			graph[(int)e].add(new long[] {s,w});
		}
		return graph;
	}

	private int ni() {
		return in.nextInt();
	}

	private long nl() {
		return in.nextLong();
	}

	private String ns() {
		return in.next();
	}

	private long[] na(int n) {
		long[] A=new long[n];
		for(int i=0;i<n;i++) {
			A[i]=in.nextLong();
		}
		return A;
	}
	private int[] nia(int n) {
		int[] A=new int[n];
		for(int i=0;i<n;i++) {
			A[i]=in.nextInt();
		}
		return A;
	}
}

class FastReader
{
	BufferedReader br;
	StringTokenizer st;

	public FastReader()
	{
		br=new BufferedReader(new InputStreamReader(System.in));
	}

	public String next()
	{
		while(st==null || !st.hasMoreElements())//回车，空行情况
		{
			try {
				st = new StringTokenizer(br.readLine());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return st.nextToken();
	}

	public int nextInt()
	{
		return Integer.parseInt(next());
	}

	public long nextLong()
	{
		return Long.parseLong(next());
	}
}