#include <bits/stdc++.h>
using namespace std;
struct P{int x,y;};
double dist(const P &a,const P &b){
double dx = double(a.x)-double(b.x);
double dy = double(a.y)-double(b.y);
return sqrt(dx*dx + dy*dy);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, K;
if(!(cin>>N>>K)) return 0;
vector<P> pts(N);
for(int i=0;i<N;i++) cin>>pts[i].x>>pts[i].y;
// grid (문제에 맞춰 10x14 = 140)
const int GW = 10, GH = 14;
const int CELLS = GW * GH; // 140
vector<vector<int>> cluster(CELLS);
const int MAXC = 814000;
for(int i=0;i<N;i++){
int gx = (long long)pts[i].x * GW / (MAXC+1);
int gy = (long long)pts[i].y * GH / (MAXC+1);
gx = max(0, min(GW-1, gx));
gy = max(0, min(GH-1, gy));
int id = gy*GW + gx;
cluster[id].push_back(i);
}
// 빈 셀 채우기: 가장 큰 셀에서 하나씩 옮김
vector<int> emptyCells;
for(int i=0;i<CELLS;i++) if(cluster[i].empty()) emptyCells.push_back(i);
auto largest_idx = [&]()->int{
int idx = 0; int mx = cluster[0].size();
for(int i=1;i<CELLS;i++) if((int)cluster[i].size() > mx){ mx = cluster[i].size(); idx = i; }
return idx;
};
while(!emptyCells.empty()){
int to = emptyCells.back();
int from = largest_idx();
if(from==to) break;
if(cluster[from].size()<=1){
int found=-1;
for(int i=0;i<CELLS;i++) if(cluster[i].size()>1){ found=i; break; }
if(found==-1) break;
from = found;
}
int mv = cluster[from].back();
cluster[from].pop_back();
cluster[to].push_back(mv);
emptyCells.pop_back();
}
// nearest-neighbor 초기화 (O(m^2) per cluster)
auto build_nn = [&](const vector<int>& idxs)->vector<int>{
int m = idxs.size();
if(m==0) return {};
vector<int> tour; tour.reserve(m);
vector<char> used(m,0);
int cur = 0;
tour.push_back(idxs[cur]);
used[cur]=1;
for(int step=1; step<m; step++){
int best = -1; double bd = 1e300;
for(int j=0;j<m;j++) if(!used[j]){
double d = dist( pts[tour.back()], pts[idxs[j]] );
if(d < bd){ bd = d; best = j; }
}
used[best]=1;
tour.push_back(idxs[best]);
}
return tour;
};
// 안정적인 2-opt (표준 구현)
auto tour_len = [&](const vector<int>& t)->double{
int m = t.size();
if(m<=1) return 0.0;
double s=0;
for(int i=0;i<m;i++){
s += dist( pts[t[i]], pts[t[(i+1)%m]] );
}
return s;
};
auto two_opt = [&](vector<int>& t, int iter_limit){
int n = t.size();
if(n<=2) return;
bool improved = true;
int it = 0;
while(improved && it < iter_limit){
improved = false;
for(int i=0;i<n-1;i++){
for(int j=i+2;j<n;j++){
if(i==0 && j==n-1) continue; // 전체 루프를 깨는 교체 방지
double before = dist(pts[t[i]], pts[t[(i+1)%n]]) + dist(pts[t[j]], pts[t[(j+1)%n]]);
double after = dist(pts[t[i]], pts[t[j]]) + dist(pts[t[(i+1)%n]], pts[t[(j+1)%n]]);
if(after + 1e-12 < before){
reverse(t.begin() + (i+1), t.begin() + (j+1));
improved = true;
goto NEXT_ITER;
}
}
}
NEXT_ITER:
it++;
}
};
// 초기 tours 구성
vector<vector<int>> tours(CELLS);
for(int i=0;i<CELLS;i++){
tours[i] = build_nn(cluster[i]);
if(tours[i].size()>1) two_opt(tours[i], 200);
}
vector<double> len(CELLS);
for(int i=0;i<CELLS;i++) len[i] = tour_len(tours[i]);
// 인접 셀 반환
auto neighbors = [&](int id)->vector<int>{
vector<int> res;
if(id < 0 || id >= CELLS) return res;
int gx = id % GW, gy = id / GW;
for(int dy=-1; dy<=1; dy++) for(int dx=-1; dx<=1; dx++){
if(dx==0 && dy==0) continue;
int nx = gx + dx, ny = gy + dy;
if(nx>=0 && nx < GW && ny>=0 && ny < GH) res.push_back(ny*GW + nx);
}
return res;
};
// 개선 루프: 최대 경로(worst cluster)에서 주변으로 도시 이동 시도
for(int iter=0; iter<250; iter++){
int worst = max_element(len.begin(), len.end()) - len.begin();
vector<int> nbs = neighbors(worst);
double bestgain = 1e-12;
int best_from_pos=-1, best_to_cell=-1, best_to_insert_pos=-1;
for(int nb : nbs){
if(nb<0 || nb>=CELLS) continue;
int m_w = tours[worst].size();
if(m_w==0) continue;
for(int pos=0; pos<m_w; pos++){
int city = tours[worst][pos];
int prev = tours[worst][ (pos-1 + m_w) % m_w ];
int next = tours[worst][ (pos+1) % m_w ];
double removeCost = dist(pts[prev], pts[city]) + dist(pts[city], pts[next]) - dist(pts[prev], pts[next]);
double bestInsert = 1e300;
int bestI = 0;
int m_nb = tours[nb].size();
if(m_nb==0){
// insert into empty nb: cost = 0 -> cycle of length 0 becomes two edges city->city (effectively 0)
bestInsert = 0.0;
bestI = 0;
} else {
for(int j=0;j<m_nb;j++){
int a = tours[nb][j];
int b = tours[nb][ (j+1)%m_nb ];
double ins = dist(pts[a], pts[city]) + dist(pts[city], pts[b]) - dist(pts[a], pts[b]);
if(ins < bestInsert){ bestInsert = ins; bestI = j+1; }
}
}
double newWorstLen = len[worst] - removeCost;
double newNbLen = len[nb] + bestInsert;
double newMax = max(newWorstLen, newNbLen);
double oldMax = max(len[worst], len[nb]);
double gain = oldMax - newMax;
if(gain > bestgain){
bestgain = gain;
best_from_pos = pos;
best_to_cell = nb;
best_to_insert_pos = bestI;
}
}
}
if(best_from_pos == -1) break;
// 수행
int city = tours[worst][best_from_pos];
tours[worst].erase(tours[worst].begin() + best_from_pos);
if(best_to_insert_pos > (int)tours[best_to_cell].size()) best_to_insert_pos = tours[best_to_cell].size();
tours[best_to_cell].insert(tours[best_to_cell].begin() + best_to_insert_pos, city);
if(tours[worst].size()>1) two_opt(tours[worst], 60);
if(tours[best_to_cell].size()>1) two_opt(tours[best_to_cell], 60);
len[worst] = tour_len(tours[worst]);
len[best_to_cell] = tour_len(tours[best_to_cell]);
}
// 출력 전 보정: 처음 K개의 슬롯이 모두 1개 이상을 가지도록 함
// (문제의 K==CELLS인 경우 이 단계에서 모두 채워져야 함)
vector<int> empties;
for(int i=0;i<K;i++) if(i < (int)tours.size() && tours[i].empty()) empties.push_back(i);
for(int e : empties){
int donor = -1, mx = 0;
for(int j=0;j<K;j++){
if(j < (int)tours.size() && (int)tours[j].size() > mx){
mx = tours[j].size(); donor = j;
}
}
if(donor==-1 || mx<=1) break;
int mv = tours[donor].back(); tours[donor].pop_back();
tours[e].push_back(mv);
}
// 중복/누락 검사: 실패 시 안전한 분배(라운드로빈)로 폴백
vector<char> seen(N,false);
bool ok = true;
for(int i=0;i<K && i<(int)tours.size(); i++){
for(int c : tours[i]){
if(c<0 || c>=N){ ok=false; break; }
if(seen[c]){ ok=false; break; }
seen[c]=true;
}
if(!ok) break;
}
for(int i=0;i<N;i++) if(!seen[i]){ ok=false; break; }
if(!ok){
// 폴백: 간단히 라운드로빈으로 분배
vector<vector<int>> fallback(K);
for(int i=0;i<N;i++) fallback[i%K].push_back(i);
for(int i=0;i<K;i++) tours[i] = fallback[i];
}
// 최종 출력: K줄 (1-based index)
for(int i=0;i<K;i++){
if(i < (int)tours.size()){
cout << tours[i].size();
for(int v : tours[i]) cout << ' ' << (v+1);
cout << '\n';
} else {
// 잘못된 상황 방어 (보통 발생하지 않음)
cout << "1 1\n";
}
}
return 0;
}