import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Main { static boolean batch = false; static boolean readFile = false; static boolean outFile = false; static int caseNum = 0; static long limitTime = 1800; static long endTime; static int border = 5; static int N, K, NN; static int[][] d, a; static int[] dx = {1, 0, 1, 1, -1, -1, 0, -1}; static int[] dy = {0, 1, 1, -1, 1, -1, -1, 0}; static int cnt; static PrintWriter pw; public static void main(String[] args) throws Exception { if (batch) { readFile = true; } if (readFile) { if (batch) { long tl = 2000; long tw = tl - 200; long total = 0; int bidx = 0; long best = 0; int widx = 0; long worst = 1000000000000000000L; int re = 0; int tle = 0; for (int z = 0; z < 100; z++) { try { long st = System.currentTimeMillis(); long score = solve(z); long time = System.currentTimeMillis() - st; if (time > tw) { System.out.println(score + "\t" + time + "ms"); if (time > tl) { tle++; } } else { System.out.println(score); } total += score; if (score > best) { best = score; bidx = z; } if (score < worst) { worst = score; widx = z; } } catch (Exception e) { System.out.println(z + ":\t" + e.getMessage()); re++; } } System.out.println("total: " + total); System.out.println("best: " + bidx + ": " + best); System.out.println("worst: " + widx + ": " + worst); System.out.println("RE: " + re); System.out.println("TLE: " + tle); } else { long score = solve(caseNum); System.out.println(cnt); System.out.println(score); } } else { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); init(br); br.close(); solve(); } } static long solve(int z) throws Exception { StringBuilder sb = new StringBuilder(); sb.append(z); while (sb.length() < 4) { sb.insert(0, '0'); } File file = new File("G:\\yuki\\5025\\in\\" + sb.toString() + ".txt"); BufferedReader br = new BufferedReader(new FileReader(file)); init(br); br.close(); if (outFile) { File file2 = new File("G:\\yuki\\5025\\out\\" + sb.toString() + ".txt"); pw = new PrintWriter(file2); } long res = solve(); if (outFile) { pw.close(); } return res; } static void init(BufferedReader br) throws Exception { endTime = System.currentTimeMillis() + limitTime; String[] sa = br.readLine().split(" "); N = Integer.parseInt(sa[0]); K = Integer.parseInt(sa[1]); NN = N * N; d = new int[N][N]; for (int i = 0; i < N; i++) { char[] s = br.readLine().toCharArray(); for (int j = 0; j < N; j++) { d[i][j] = s[j] - '0'; } } a = new int[N][N]; cnt = 0; } static long solve() { Ans ans = solve1(); while (System.currentTimeMillis() < endTime) { Ans res = solve1(); if (res.c < ans.c) { ans = res; } cnt++; } if (outFile) { output(pw, ans); } else if (!batch) { output(new PrintWriter(System.out), ans); } long score = ans.c; return score; } static Ans solve1() { for (int i = 0; i < N; i++) { System.arraycopy(d[i], 0, a[i], 0, N); } List list = new ArrayList<>(); while (list.size() < K) { int p = (int) (Math.random() * NN); int cx = p / N; int cy = p % N; while (a[cx][cy] < border) { p = (int) (Math.random() * NN); cx = p / N; cy = p % N; } int dr = (int) (Math.random() * 8); for (int i = 0; i < N && list.size() < K; i++) { int nx = cx + dx[dr] * i; int ny = cy + dy[dr] * i; if (nx < 0 || N <= nx || ny < 0 || N <= ny || a[nx][ny] < border) { break; } a[nx][ny] = -1; list.add(toInt(nx, ny)); } dr = 7 - dr; for (int i = 1; i < N && list.size() < K; i++) { int nx = cx + dx[dr] * i; int ny = cy + dy[dr] * i; if (nx < 0 || N <= nx || ny < 0 || N <= ny || a[nx][ny] < border) { break; } a[nx][ny] = -1; list.add(toInt(nx, ny)); } } DSU uf = new DSU(NN); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (j > 0 && a[i][j - 1] >= border && a[i][j] >= border) { uf.merge(toInt(i, j - 1), toInt(i, j)); } if (i > 0 && a[i - 1][j] >= border && a[i][j] >= border) { uf.merge(toInt(i - 1, j), toInt(i, j)); } } } List> grps = uf.groups(); int c = 0; for (List grp : grps) { int size = grp.size(); if (size > 1) { c += size * size; } } Ans ans = new Ans(); ans.c = c; ans.list = list; return ans; } static int toInt(int x, int y) { return x * N + y; } static void output(PrintWriter pw, Ans ans) { pw.println(ans.list.size()); for (int p : ans.list) { int x = p / N; int y = p % N; pw.println(x + " " + y); } pw.flush(); } static class Ans { List list; int c; } } class DSU { private int n; private int[] parentOrSize; private int num; /** * n頂点0辺の無向グラフを作る。
* O(n) * * @param n 頂点数 */ public DSU(int n) { this.n = n; this.parentOrSize = new int[n]; Arrays.fill(parentOrSize, -1); num = n; } /** * 辺(a, b)を足す。
* ならしO(α(n)) * * @param a 頂点番号(0≦a<n) * @param b 頂点番号(0≦b<n) * @return 代表元 */ int merge(int a, int b) { assert 0 <= a && a < n : "a=" + a; assert 0 <= b && b < n : "b=" + b; int x = leader(a); int y = leader(b); if (x == y) { return x; } if (-parentOrSize[x] < -parentOrSize[y]) { int tmp = x; x = y; y = tmp; } parentOrSize[x] += parentOrSize[y]; parentOrSize[y] = x; num--; return x; } /** * 頂点a, bが連結かどうか。
* ならしO(α(n)) * * @param a 頂点番号(0≦a<n) * @param b 頂点番号(0≦b<n) * @return true:連結 false:非連結 */ boolean same(int a, int b) { assert 0 <= a && a < n : "a=" + a; assert 0 <= b && b < n : "b=" + b; return leader(a) == leader(b); } /** * 頂点aの属する連結成分の代表元を返す。
* ならしO(α(n)) * * @param a 頂点番号(0≦a<n) * @return 代表元 */ int leader(int a) { assert 0 <= a && a < n : "a=" + a; if (parentOrSize[a] < 0) { return a; } else { return parentOrSize[a] = leader(parentOrSize[a]); } } /** * 頂点aの属する連結成分の要素数を返す。
* ならしO(α(n)) * * @param a 頂点番号(0≦a<n) * @return 要素数 */ int size(int a) { assert 0 <= a && a < n : "a=" + a; return -parentOrSize[leader(a)]; } /** * 連結成分の数を返す。
* O(1) * * @return 連結成分数 */ int num() { return num; } /** * グラフを連結成分に分けた情報を返す。
* O(n) * * @return 「1つの連結成分の頂点番号のリスト」のリスト */ List> groups() { int[] leaderBuf = new int[n]; int[] groupSize = new int[n]; for (int i = 0; i < n; i++) { leaderBuf[i] = leader(i); groupSize[leaderBuf[i]]++; } List> result = new ArrayList<>(n); for (int i = 0; i < n; i++) { result.add(new ArrayList<>(groupSize[i])); } for (int i = 0; i < n; i++) { result.get(leaderBuf[i]).add(i); } result.removeIf(List::isEmpty); return result; } }