#include <bits/stdc++.h>

using namespace std;

struct Point{
    double x, y;
    static constexpr double EPS = 1e-9;

    Point(double x, double y) : x(x), y(y) {}
    Point(): x(0), y(0) {}

    Point operator+(const Point& other){
        return Point(x+other.x, y+other.y);
    }
    bool operator ==(Point a) {
	return abs(a.x - x) < EPS && abs(a.y - y) < EPS;
    }
    Point operator-(const Point& other){
        return Point(x-other.x, y-other.y);
    }
    Point operator*(double scalar){
        return Point(x*scalar, y*scalar);
    }

    Point operator/(double scalar){
        return Point(x/scalar, y/scalar);
    }

    void print() {
	cout << (int) round(x) << " " << (int) round(y) << "\n";
    }

    Point rotate(double ang) {
	return Point(x*cos(ang) - y*sin(ang), x*sin(ang) + y*cos(ang));
    }

    double angle() {
	    return atan2(y, x);
    }

    double dot(Point a) {
	return x*a.x + y*a.y;
    }


};

double S;


double sqdist(Point a, Point b){
return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y);
}

double dist(Point a, Point b){
return sqrt(sqdist(a, b));
}

double unit_norm(Point a, Point b) {
	if (a == Point() || b == Point()) return 0;
	a = a/dist(Point(), a);
	b = b/dist(Point(), b);
	return a.dot(b);
}

double angle(Point a, Point b, Point c) {
	Point p1 = a - b;
	Point p2 = c - b;
	auto a1 = p1.angle();
	p2 = p2.rotate(-a1);

	return p2.angle();
}

Point toricelly(Point& A, Point& B, Point& C) {
	Point res = (A + B + C) / 3;
	double eps = 1e-2;
	
	while (true) {
		double dA = dist(A, res); //TODO
		double dB = dist(B, res);
		double dC = dist(C, res);

		Point delta = (A - res) / dA + (B - res) / dB + (C - res) / dC;

		if (dist(delta, Point()) < eps) break;

		res = res + delta;
	}
	return res;
}


struct SteinerTree{
    int N;
    vector<Point> points;
    vector<vector<int>> adj;

    SteinerTree(int N) : N(N) {
	points.reserve(N);
    }

    struct UnionFind2 {
        vector<int> par;
        UnionFind2(int n) : par(n, -1) {}
        
        int find(int a) {
            if(par[a] <= -1) return a;
            while(par[par[a]] >= 0) {
                par[a] = par[par[a]];
            }
            return par[a];
        }

        void unite(int a, int b) {
            a = find(a);
            b = find(b);

	    if(a == b) return;

            if(-par[a] > -par[b]) swap(a, b);
            par[b] += par[a];
            par[a] = b;
        }
    };

    struct UnionFind {
	    vector<int> par;
	    vector<int> sz;
	    UnionFind(int n){
		    for(int i=0; i<n; i++) par.push_back(i);
		    sz.resize(n, 1);
	    }
	    int find(int a){
		    int c = a;
		    while(par[c] != c) c=par[c];

		    while(par[a] != c) {
			    int nxt = par[a];
			    par[a] = c;
			    a = nxt;
		    }
		    return c;
	    }
	    void unite(int a, int b){
		    a=find(a);
		    b=find(b);


		    if(a==b) return;

		    if(sz[a] < sz[b]) swap(a,b);

		    par[a]=b;
		    sz[a]+=sz[b];
	    }
    };

    vector<vector<int>> spanning_tree() {
        vector<pair<double, pair<int,int>>> edges;
        for(int i = 0; i < points.size(); i++){
            for(int j = 0; j < i; j++){
                double dist = sqdist(points[i], points[j]);
                edges.push_back({dist, {i,j}});
            }
        }
        sort(edges.begin(), edges.end());

        UnionFind uf(points.size());

        vector<vector<int>> res(points.size());
        for(auto [dist, ends] : edges){
            auto [a,b] = ends;
            if(uf.find(a) != uf.find(b)){
                uf.unite(a,b);
                res[a].push_back(b);
                res[b].push_back(a);
            }
        }

        return res;
    }

    SteinerTree(vector<Point> pnts) : N(pnts.size()), points(pnts), adj(pnts.size()) {
    }

    double cost() {
	double c = 0;

	for (int i = 0; i < adj.size();i++) for (int j : adj[i]) {
		c += dist(points[i], points[j]);
	}

	return c/2 + S*(points.size() - N);
    }

    void print() {
	cout << points.size() - N << " " << points.size() - 1 << "\n";

	for (int i = N; i < points.size();i++) points[i].print();

	for (int i = 0; i < adj.size(); i++) {
		for (int j : adj[i]) if (i < j) {
			cout << i +  1 << " " << j + 1 << "\n";
		}
	}
    }

};

bool step(SteinerTree& st) {
	double min_norm = 1e9;
	int bi, bj, bk;
	for (int i = 0; i < st.points.size(); i++) {
		for (int _j = 0;_j < st.adj[i].size(); _j++) for(int _k = _j+1; _k < st.adj[i].size(); _k++) {
			int j = st.adj[i][_j];
			int k = st.adj[i][_k];

			double norm = unit_norm(st.points[j] - st.points[i], st.points[k] - st.points[i]);
			if (min_norm > norm) {
				bi = i;
				bj = j;
				bk = k;
				min_norm = norm;
			}
		}
	}

	Point T = toricelly(st.points[bi], st.points[bj], st.points[bk]);

	st.points.push_back(T);

	auto cost = st.cost();
	auto new_adj = st.spanning_tree();
	auto old = std::move(st.adj);

	st.adj = std::move(new_adj);
	auto nw_cost = st.cost();

	if (nw_cost > cost) {
		st.adj = std::move(old);
		return 0;
	}
	return 1;
}

int main()
{
	int N;    
	cin >> N >> S;
	if(N==0){
		cout << "0 0\n";
		return 0;
	}
	vector<Point> pts;
	for (int i = 0; i < N;i ++) {
		double x, y; cin >> x >> y;
		pts.push_back({x, y});
	}

	SteinerTree st(pts);

	auto tree = st.spanning_tree();
	st.adj = tree;

	st.print();
}
