#pragma GCC optimize("O3") #pragma GCC optimize("unroll-loops") #pragma GCC target("avx2,bmi,bmi2,lzcnt,popcnt") #include #include #include #include #include #include #include #include #include using namespace std; // --- SA.cppからの移植: 高速乱数と指数関数LUT --- static uint64_t s_rng[2] = {123456789, 362436069}; inline uint64_t rng64() { uint64_t s1 = s_rng[0], s0 = s_rng[1]; s_rng[0] = s0; s1 ^= s1 << 23; s_rng[1] = s1 ^ s0 ^ (s1 >> 17) ^ (s0 >> 26); return s_rng[1] + s0; } inline uint32_t rand_xor() { return rng64() >> 32; } inline int rand_int(int n) { return (uint64_t(rand_xor()) * n) >> 32; } constexpr int EXP_LUT_SIZE = 1024; double exp_lut[EXP_LUT_SIZE + 1]; inline double fast_exp(double x) { if (x <= 0) return 1.0; if (x >= 8.0) return 0.0; int idx = (int)(x / 8.0 * EXP_LUT_SIZE + 0.5); if (idx < 0) idx = 0; if (idx > EXP_LUT_SIZE) idx = EXP_LUT_SIZE; return exp_lut[idx]; } // --------------------------------------------- constexpr int N = 50; constexpr int K = 100; struct DSU { vector parent_or_size; DSU(int n) : parent_or_size(n, -1) {} int leader(int a) { if (parent_or_size[a] < 0) return a; return parent_or_size[a] = leader(parent_or_size[a]); } void merge(int a, int b) { int x = leader(a), y = leader(b); if (x == y) return; if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y); parent_or_size[x] += parent_or_size[y]; parent_or_size[y] = x; } }; int D[N][N]; int dx[] = {0, 1, 0, -1}; int dy[] = {1, 0, -1, 0}; long long calc_score(const vector>& L) { vector> in_S(N, vector(N, false)); for (const auto& p : L) { in_S[p.first][p.second] = true; } DSU ds(N * N); for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { if (in_S[i][j] || D[i][j] < 5) continue; int ij = i * N + j; for (int k = 0; k < 4; ++k) { int x2 = i + dx[k]; int y2 = j + dy[k]; if (x2 >= 0 && x2 < N && y2 >= 0 && y2 < N) { if (!in_S[x2][y2] && D[x2][y2] >= 5) { int xy2 = x2 * N + y2; ds.merge(ij, xy2); } } } } } long long s = 0; for (int i = 0; i < N * N; ++i) { if (ds.parent_or_size[i] < 0) { long long sz = -ds.parent_or_size[i]; s += sz * sz; } } return s; } int main() { // 指数関数のLUT初期化 for (int i = 0; i <= EXP_LUT_SIZE; i++) { exp_lut[i] = exp(-i * 8.0 / EXP_LUT_SIZE); } ios_base::sync_with_stdio(false); cin.tie(NULL); int dummy_n, dummy_k; if (!(cin >> dummy_n >> dummy_k)) return 0; for (int i = 0; i < N; ++i) { string S; cin >> S; for (int j = 0; j < N; ++j) { D[i][j] = S[j] - '0'; } } vector>> Subs; vector> small_elements; // 既存の走査1 for (int i = 1; i < N - 1; ++i) { vector> L; for (int x = 0; x < N; ++x) { if (D[x][i + x % 2] >= 5) { L.push_back({x, i + x % 2}); } else { if (L.size() > 5) { Subs.push_back(L); } else if (!L.empty()) { for (const auto& p : L) small_elements.push_back(p); } L.clear(); } } if (!L.empty()) { if (L.size() > 5) { Subs.push_back(L); } else { for (const auto& p : L) small_elements.push_back(p); } } } // 既存の走査2 for (int i = 1; i < N - 1; ++i) { vector> L; for (int x = 0; x < N; ++x) { if (D[i + x % 2][x] >= 5) { L.push_back({i + x % 2, x}); } else { if (L.size() > 5) { Subs.push_back(L); } else if (!L.empty()) { for (const auto& p : L) small_elements.push_back(p); } L.clear(); } } if (!L.empty()) { if (L.size() > 5) { Subs.push_back(L); } else { for (const auto& p : L) small_elements.push_back(p); } } } // x + y == k の斜め走査 (kは 0 から 2*N-2 まで) for (int k = 0; k <= 2 * N - 2; ++k) { vector> L; int start_x = max(0, k - N + 1); int end_x = min(N - 1, k); for (int x = start_x; x <= end_x; ++x) { int y = k - x; if (D[x][y] >= 5) { L.push_back({x, y}); } else { if (L.size() > 5) { Subs.push_back(L); } else if (!L.empty()) { for (const auto& p : L) small_elements.push_back(p); } L.clear(); } } if (!L.empty()) { if (L.size() > 5) { Subs.push_back(L); } else { for (const auto& p : L) small_elements.push_back(p); } } } // x - y == k の斜め走査 (kは -(N-1) から N-1 まで) for (int k = -(N - 1); k <= N - 1; ++k) { vector> L; int start_x = max(0, k); int end_x = min(N - 1, k + N - 1); for (int x = start_x; x <= end_x; ++x) { int y = x - k; if (D[x][y] >= 5) { L.push_back({x, y}); } else { if (L.size() > 5) { Subs.push_back(L); } else if (!L.empty()) { for (const auto& p : L) small_elements.push_back(p); } L.clear(); } } if (!L.empty()) { if (L.size() > 5) { Subs.push_back(L); } else { for (const auto& p : L) small_elements.push_back(p); } } } auto t0 = chrono::steady_clock::now(); mt19937 rng(51); vector current_Chooses; int current_total_n = 0; long long current_score = calc_score({}); vector> best_ans; long long best_score = current_score; vector P(Subs.size()); iota(P.begin(), P.end(), 0); // --- 焼きなまし用パラメータ --- double start_temp = 1000.0; double end_temp = 0.0001; const double tl = 1.9; int cnt = 0; double temp = start_temp; // ------------------------------ while (true) { cnt++; auto now = chrono::steady_clock::now(); double elapsed = chrono::duration(now - t0).count(); // 一定回数ごとに温度と時間を更新 if ((cnt & 127) == 0) { if (elapsed >= tl) break; temp = start_temp * pow(end_temp / start_temp, elapsed / tl); } vector next_Chooses = current_Chooses; int next_total_n = current_total_n; if (!next_Chooses.empty()) { // 高速な独自乱数に置き換え int ind = rand_int((int)next_Chooses.size()); int val = next_Chooses[ind]; next_total_n -= Subs[val].size(); auto it = find(next_Chooses.begin(), next_Chooses.end(), val); if (it != next_Chooses.end()) { next_Chooses.erase(it); } } shuffle(P.begin(), P.end(), rng); for (int i : P) { if (next_total_n + (int)Subs[i].size() < K) { next_Chooses.push_back(i); next_total_n += Subs[i].size(); } } vector> L; for (int i : next_Chooses) { for (const auto& p : Subs[i]) { L.push_back(p); } } long long new_score = calc_score(L); long long diff = new_score - current_score; // 最小化なので diff が負なら改善 // 焼きなましの受容判定 bool accept = false; if (diff <= 0) { accept = true; } else { double prob = fast_exp((double)diff / temp); if (rand_int(1000000) < prob * 1000000) { accept = true; } } if (accept) { current_score = new_score; current_Chooses = next_Chooses; current_total_n = next_total_n; if (new_score < best_score) { best_score = new_score; best_ans = L; } } } set> final_ans; for (const auto& p : best_ans) { final_ans.insert(p); } shuffle(small_elements.begin(), small_elements.end(), rng); for (const auto& p : small_elements) { if (final_ans.size() >= K) break; final_ans.insert(p); } cout << final_ans.size() << "\n"; for (const auto& p : final_ans) { cout << p.first << " " << p.second << "\n"; } return 0; }