#include <bits/stdc++.h>
using namespace std;
const long long N = 3e3 + 5;
long long n, m, x, y, s, res[N][N], num[N], id, val, ans[N];
long long query(long long x, long long y) {
  if (x > y) swap(x, y);
  if (~res[x][y]) return res[x][y];
  printf("? %lld %lld\n", x, y), fflush(stdout);
  return scanf("%lld", &res[x][y]), res[x][y];
}
long long find(long long x) {
  long long ans = 0;
  for (long long i = 0; i <= m; i++)
    if (x != num[i] && ((query(x, num[i]) >> i) & 1)) ans |= (1 << i);
  return ans;
}
signed main() {
  srand(time(0));
  scanf("%lld", &n), m = log2(n - 1);
  for (long long i = 1; i <= n; i++) {
    ans[i] = -1;
    for (long long j = 1; j <= n; j++) res[i][j] = -1;
  }
  for (long long i = 0; i <= m; i++) num[i] = -1;
  for (long long i = 0; i <= m; i++)
    while (num[i] == -1) {
      x = rand() % n + 1;
      while ((y = rand() % n + 1) == x)
        ;
      s = query(x, y);
      for (long long j = i; j <= m; j++)
        if (!((s >> j) & 1)) num[j] = x;
    }
  val = ans[id = 1] = find(1);
  for (long long i = 2; i <= n; i++)
    if ((s = query(id, i) == val)) val = ans[id = i] = find(i);
  for (long long i = 2; i <= n; i++)
    if (ans[i] == -1) ans[i] = query(i, id);
  printf("! ");
  for (long long i = 1; i <= n; i++)
    printf("%lld%c", ans[i], i == n ? '\n' : ' ');
  return 0;
}
