import java.io.*; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.InputMismatchException; import java.util.stream.Collectors; import java.util.stream.IntStream; /** * Built using CHelper plug-in * Actual solution is at the top */ public class MaximumClique_lewin { public static void main(String[] args) { InputStream inputStream = System.in; OutputStream outputStream = System.out; InputReader in = new InputReader(inputStream); OutputWriter out = new OutputWriter(outputStream); MaximumClique solver = new MaximumClique(); solver.solve(1, in, out); out.close(); } static class MaximumClique { public int mod = 1000000007; public int[][] comb; public void solve(int testNumber, InputReader in, OutputWriter out) { int n = in.nextInt(); int c = 300; if (n == 1) { out.println(1); return; } comb = Utils.getComb(n + 1, mod); int[][] color = new int[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { color[i][j] = in.nextInt(); } } HashSet active = IntStream.range(0, n).boxed().collect(Collectors.toCollection(HashSet::new)); ArrayList cs = new ArrayList<>(); while (active.size() > 0) { int cc = -1; HashSet toRemove = new HashSet<>(); for (int x : active) { HashSet ss = active.stream().filter(y -> y != x).map(y -> color[x][y]).collect(Collectors.toCollection(HashSet::new)); if (ss.size() == 1) { toRemove.add(x); cc = ss.toArray(new Integer[1])[0]; } } active.removeAll(toRemove); cs.add(new MaximumClique.Clique(cc, toRemove.size())); } long[][] nways = new long[c + 1][n + 1]; for (int i = 1; i <= c; i++) Arrays.fill(nways[i], 1); long ans = 0; for (MaximumClique.Clique cc : cs) { long[] xx = new long[n + 1]; Arrays.fill(xx, 1); for (int i = 1; i <= c; i++) { if (i == cc.color) continue; for (int j = 1; j <= n; j++) { xx[j] = xx[j] * nways[i][j - 1] % mod; } } long[] ff = new long[n + 1]; for (int take = 1; take <= cc.size; take++) { for (int j = take; j <= n; j++) { ff[j] = (ff[j] + xx[j] * nways[cc.color][j - take] % mod * comb[cc.size][take]) % mod; } } rsum(ff); for (int i = 1; i <= n; i++) { ans = (ans + ff[i] * i) % mod; } rsum(nways[cc.color]); for (int nhave = n; nhave >= 0; nhave--) { for (int take = 1; take <= cc.size; take++) { int have = nhave - take; if (have >= 0 && nways[cc.color][have] != 0) { nways[cc.color][nhave] = (nways[cc.color][nhave] + nways[cc.color][have] * comb[cc.size][take]) % mod; } } } psum(nways[cc.color]); } out.println(ans); } public void psum(long[] x) { for (int i = 1; i < x.length; i++) { x[i] += x[i - 1]; if (x[i] >= mod) x[i] -= mod; } } public void rsum(long[] x) { for (int i = x.length - 1; i > 0; i--) { x[i] -= x[i - 1]; if (x[i] < 0) x[i] += mod; } } static class Clique { public int color; public int size; public Clique(int color, int size) { this.color = color; this.size = size; } } } 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 close() { writer.close(); } public void println(long i) { writer.println(i); } public void println(int i) { writer.println(i); } } static class Utils { public static int[][] getComb(int sz, int mod) { int[][] comb = new int[sz][sz]; for (int i = 0; i < sz; i++) { comb[i][0] = 1; for (int j = 1; j <= i; j++) { comb[i][j] = comb[i - 1][j] + comb[i - 1][j - 1]; if (comb[i][j] >= mod) comb[i][j] -= mod; } } return comb; } } static class InputReader { private InputStream stream; private byte[] buf = new byte[1024]; private int curChar; private int numChars; public InputReader(InputStream stream) { this.stream = stream; } public int read() { if (this.numChars == -1) { throw new InputMismatchException(); } else { if (this.curChar >= this.numChars) { this.curChar = 0; try { this.numChars = this.stream.read(this.buf); } catch (IOException var2) { throw new InputMismatchException(); } if (this.numChars <= 0) { return -1; } } return this.buf[this.curChar++]; } } public int nextInt() { int c; for (c = this.read(); isSpaceChar(c); c = this.read()) { ; } byte sgn = 1; if (c == 45) { sgn = -1; c = this.read(); } int res = 0; while (c >= 48 && c <= 57) { res *= 10; res += c - 48; c = this.read(); if (isSpaceChar(c)) { return res * sgn; } } throw new InputMismatchException(); } public static boolean isSpaceChar(int c) { return c == 32 || c == 10 || c == 13 || c == 9 || c == -1; } } }