#include #include #include #include int main() { int n, dim; std::cin >> n >> dim; std::vector> a(n, std::vector(dim + 1, 0)); for (int i = 0; i < n; i++) { a[i][0] = i + 1; for (int j = 1; j < dim + 1; j++) { std::cin >> a[i][j]; a[i][j] *= 2; } } int* b = new int[dim + 1](); int mid1, mid2; for (int i = 1; i < dim + 1; i++) { std::nth_element(a.begin(), a.begin() + n / 2, a.end(), [&](const std::vector& left, const std::vector& right) { return left[i] < right[i]; } ); std::nth_element(a.begin(), a.begin() + n / 2 - 1, a.begin() + n / 2, [&](const std::vector& left, const std::vector& right) { return left[i] < right[i]; } ); mid1 = a[n / 2 - 1][i]; mid2 = a[n / 2][i]; b[i] = int((long long (mid1) + long long(mid2)) / 2); } int powdim = pow(3, dim); std::vector> group(powdim); int code, digit; for (int i = 0; i < n; i++) { code = 0; for (int j = 1; j < dim + 1; j++) { digit = (a[i][j] < b[j]) ? 0 : ((a[i][j] == b[j]) ? 1 : 2); code = code * 3 + digit; } group[code].push_back(a[i][0]); } const int SOURCE = powdim; const int SINK = powdim + 1; int** cap = new int*[powdim + 2]; int** flow = new int*[powdim + 2]; std::vector> next(powdim + 2); for (int i = 0; i < powdim + 2; i++) { cap[i] = new int[powdim + 2](); flow[i] = new int[powdim + 2](); } int fstdigit; for (code = 0; code < powdim; code++) { fstdigit = code / (powdim / 3); if (!group[code].empty()) { if (fstdigit == 0) { cap[SOURCE][code] = group[code].size(); next[SOURCE].push_back(code); next[code].push_back(SOURCE); } else { cap[code][SINK] = group[code].size(); next[code].push_back(SINK); next[SINK].push_back(code); } } } bool compat; int cop1, cop2, dig1, dig2; for (int c1 = 0; c1 < powdim / 3; c1++) { for (int c2 = 2 * powdim / 3; c2 < powdim; c2++) { cop1 = c1; cop2 = c2; compat = true; for (int i = 1; i < dim + 1; i++) { dig1 = cop1 % 3; dig2 = cop2 % 3; cop1 /= 3; cop2 /= 3; if ((dig1 == 0 && dig2 == 0) || (dig1 == 2 && dig2 == 2)) { compat = false; break; } } if (compat) { cap[c1][c2] = std::min(group[c1].size(), group[c2].size()); next[c1].push_back(c2); next[c2].push_back(c1); } } } int* height = new int[powdim + 2](); int* excess = new int[powdim + 2](); for (int c = 0; c < powdim; c++) height[SOURCE] += (!group[c].empty()) ? 1 : 0; height[SOURCE]++; std::vector> heightExcessCodes(1 + 2 * height[SOURCE]); for (int c = 0; c < powdim; c++) { if (cap[SOURCE][c] > 0) { excess[c] = cap[SOURCE][c]; flow[SOURCE][c] = cap[SOURCE][c]; flow[c][SOURCE] = -cap[SOURCE][c]; heightExcessCodes[height[c]].push_back(c); } } int h, f, extra, minHeight; while (true) { h = heightExcessCodes.size() - 1; while (h >= 0 && heightExcessCodes[h].empty()) h--; if (h < 0) break; f = heightExcessCodes[h].back(); heightExcessCodes[h].pop_back(); while (true) { for (int to : next[f]) { if (height[to] < height[f] && flow[f][to] < cap[f][to]) { extra = std::min(cap[f][to] - flow[f][to], excess[f]); if ((excess[to] == 0) && to != SINK && to != SOURCE) heightExcessCodes[height[to]].push_back(to); flow[f][to] += extra; flow[to][f] = -flow[f][to]; excess[f] -= extra; excess[to] += extra; if (excess[f] == 0) break; } } if (excess[f] == 0) break; minHeight = 2 * height[SOURCE]; for (int to : next[f]) { if (flow[f][to] < cap[f][to]) minHeight = std::min(minHeight, height[to]); } height[f] = minHeight + 1; } } if (excess[SINK] != n / 2) { std::cout << "NO"; return 0; } std::cout << "YES" << '\n'; int id1, id2; for (int c1 = 0; c1 < powdim / 3; c1++) { for (int c2 = 2 * powdim / 3; c2 < powdim; c2++) { while (flow[c1][c2] > 0) { flow[c1][c2]--; id1 = group[c1].back(); group[c1].pop_back(); id2 = group[c2].back(); group[c2].pop_back(); std::cout << id1 << ' ' << id2 << '\n'; } } } delete[] b; delete[] height; delete[] excess; for (int i = 0; i < powdim + 2; i++) { delete[] flow[i]; delete[] cap[i]; } delete[] flow; delete[] cap; return 0; }