#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
inline long long query(int id, long long x) {
  if (!x) return 0;
  printf("? %d %lld\n", id, x);
  fflush(stdout);
  long long ret;
  scanf("%lld", &ret);
  return ret;
}
inline long long findx(int id, long long L, long long R, long long v) {
  long long mid;
  while (L < R) {
    mid = (L + R) >> 1;
    long long val = query(id, mid);
    if (val < v)
      L = mid + 1;
    else if (v < val)
      R = mid - 1;
    else
      return mid;
  }
  return L;
}
long long tim, vis[N];
long long getmid(vector<int> &f, vector<int> &g, long long L, long long R,
                 long long v, int cnt) {
  int id = f[rand() % f.size()];
  long long x = findx(id, L, R, v), val;
  vector<int> l, r, p;
  for (int i = 0; i < g.size(); ++i) {
    if (g[i] == id) continue;
    val = query(g[i], x);
    if (val < v)
      l.push_back(g[i]);
    else if (val > v)
      r.push_back(g[i]);
    else
      p.push_back(g[i]);
  }
  while (l.size() < cnt && !p.empty()) l.push_back(p.back()), p.pop_back();
  while (p.size()) r.push_back(p.back()), p.pop_back();
  if (l.size() <= cnt && r.size() < g.size() - cnt) {
    f.clear();
    return x;
  }
  if (l.size() > cnt) {
    ++tim;
    for (int i = 0; i < l.size(); ++i) vis[l[i]] = tim;
    for (int i = 0; i < f.size(); i++)
      if (vis[f[i]] != tim) swap(f[i], f.back()), f.pop_back(), i--;
    return getmid(f, g, L, R, v, cnt);
  } else {
    ++tim;
    for (int i = 0; i < r.size(); ++i) vis[r[i]] = tim;
    for (int i = 0; i < f.size(); i++)
      if (vis[f[i]] != tim) swap(f[i], f.back()), f.pop_back(), i--;
    return getmid(f, g, L, R, v, cnt);
  }
}
struct Interval {
  long long l, r;
} ans[N];
void work(vector<int> &f, long long L, long long R, long long xl,
          long long xr) {
  if (!f.size()) return;
  if (f.size() == 1) {
    ans[f[0]].l = xl;
    ans[f[0]].r = xr;
    return;
  }
  long long m = f.size();
  long long d = (R - L) / m;
  vector<int> g = f;
  long long x = getmid(g, f, xl, xr, L + d * (m / 2), m >> 1);
  long long val;
  vector<int> l, r, p;
  for (int i = 0; i < f.size(); ++i) {
    val = query(f[i], x);
    if (val > L + d * (m / 2))
      l.push_back(f[i]);
    else if (val < L + d * (m / 2))
      r.push_back(f[i]);
    else
      p.push_back(f[i]);
  }
  while (l.size() < (m >> 1) && p.size()) l.push_back(p.back()), p.pop_back();
  while (!p.empty()) r.push_back(p.back()), p.pop_back();
  work(l, L, L + d * (m / 2), xl, x);
  work(r, L + d * (m / 2), R, x, xr);
}
vector<int> f;
int n;
long long L;
int main() {
  scanf("%d %lld", &n, &L);
  for (int i = 1; i <= n; ++i) f.push_back(i);
  work(f, 0, L, 0, 1e18);
  putchar('!');
  putchar('\n');
  for (int i = 1; i <= n; ++i) printf("%lld %lld\n", ans[i].l, ans[i].r);
  return 0;
}
