#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-9;
int MAXB = 10;
const int MAXN = 1025;
vector<int> distGroup[MAXN];
int hasDist[MAXN];
int predecessor[MAXN];
vector<int> output;
set<int> centers;
string result, resultm;
int maxDist[MAXN];
int minDist[MAXN];
vector<string> strangeresults;
int main() {
  int N;
  cin >> N;
  while ((1 << MAXB) > 2 * N) --MAXB;
  for (int i = (0); i < (N); i++) minDist[i] = 1;
  for (int i = (0); i < (N); i++) maxDist[i] = (1 << MAXB) - 1;
  for (int i = (0); i < (N); i++) hasDist[i] = -1;
  for (int i = (0); i < (N); i++) distGroup[i].clear();
  distGroup[0].push_back(0);
  maxDist[0] = minDist[0] = hasDist[0] = 0;
  int largest = 0;
  for (int p = int(MAXB) - 1; p >= (0); p--) {
    for (int s = (0); s < (2); s++) {
      output = vector<int>(N, 0);
      centers.clear();
      for (int i = (s << (p + 1)); true; i += (4 << p)) {
        if (i > N || int((distGroup[i]).size()) == 0) break;
        for (auto n : distGroup[i]) {
          assert(output[n] == 0);
          output[n] = (1 << p);
        }
        centers.insert(i);
      }
      if (int((centers).size()) > 0) {
        cout << "?";
        for (auto v : output) cout << " " << max(0, min(N - 1, v - 1));
        cout << endl << flush;
        cin >> resultm;
        resultm[0] = '1';
        if (resultm == string(N, '1')) {
          result = resultm;
        } else {
          cout << "?";
          for (auto v : output) cout << " " << min(N - 1, v);
          cout << endl << flush;
          cin >> result;
        }
        while (int((result).size()) != N || int((resultm).size()) != N) {
          cerr << "VERY STRANGE!! got \"" << result << "\" and \"" << resultm
               << "\" as results" << endl
               << flush;
          strangeresults.push_back(result);
          strangeresults.push_back(resultm);
          if (int((strangeresults).size()) == (N - 1) * 2) {
            cout << "!" << endl;
            for (int i = (0); i < (N - 1); i++)
              cout << strangeresults[i * 2] << " " << strangeresults[i * 2 + 1]
                   << endl;
            return 0;
          }
          cout << "?";
          for (auto v : output) cout << " " << min(N - 1, v);
          cout << endl << flush;
          cin >> result;
          cout << "?";
          for (auto v : output) cout << " " << max(0, min(N - 1, v - 1));
          cout << endl << flush;
          cin >> resultm;
        }
        for (int i = (0); i < (N); i++) {
          if (minDist[i] == maxDist[i]) continue;
          if (centers.find(minDist[i] - 1) != centers.end()) {
            if (result[i] == '1') {
              if (resultm[i] == '1') {
                maxDist[i] -= (1 << p);
              } else {
                minDist[i] = maxDist[i] = minDist[i] - 1 + (1 << p);
              }
            } else
              minDist[i] += (1 << p);
          }
          if (minDist[i] == maxDist[i]) {
            hasDist[i] = minDist[i];
            largest = max(largest, minDist[i]);
            distGroup[minDist[i]].push_back(i);
          }
        }
      }
    }
  }
  for (int i = (0); i < (N); i++) predecessor[i] = 0;
  for (int i = (0); i < (N); i++)
    assert(minDist[i] == maxDist[i] && maxDist[i] == hasDist[i]);
  for (int a = (0); a < (2); a++) {
    for (int b = (0); b < (MAXB); b++) {
      output = vector<int>(N, 0);
      for (int dd = 2 * a; dd <= largest; dd += 4) {
        for (int d = (dd); d < (dd + 2); d++) {
          if (d > largest) continue;
          for (int i = (0); i < (int((distGroup[d]).size())); i++)
            if ((i >> b) & 1) output[distGroup[d][i]] = 1;
        }
      }
      if (output == vector<int>(N, 0)) continue;
      cout << "?";
      for (auto v : output) cout << " " << v;
      cout << endl << flush;
      cin >> result;
      for (int dd = 1 + 2 * a; dd <= largest; dd += 4) {
        for (int d = (dd); d < (dd + 2); d++) {
          if (d > largest) continue;
          for (auto n : distGroup[d])
            if (result[n] == '1') predecessor[n] |= 1 << b;
        }
      }
    }
  }
  for (int i = (1); i < (N); i++)
    predecessor[i] = distGroup[hasDist[i] - 1][predecessor[i]];
  cout << "!" << endl << flush;
  for (int i = (1); i < (N); i++)
    cout << predecessor[i] + 1 << " " << i + 1 << endl;
  return 0;
}
