import java.util.*; import java.io.BufferedReader; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.OutputStream; class par { int x, y, z, w; public par(int _x, int _y, int _z, int _w) { this.x = _x; this.y = _y; this.z = _z; this.w = _w; } } class Kattio extends PrintWriter { public Kattio(InputStream i) { super(new BufferedOutputStream(System.out)); r = new BufferedReader(new InputStreamReader(i)); } public Kattio(InputStream i, OutputStream o) { super(new BufferedOutputStream(o)); r = new BufferedReader(new InputStreamReader(i)); } public boolean hasMoreTokens() { return peekToken() != null; } public int getInt() { return Integer.parseInt(nextToken()); } public double getDouble() { return Double.parseDouble(nextToken()); } public long getLong() { return Long.parseLong(nextToken()); } public String getWord() { return nextToken(); } private BufferedReader r; private String line; private StringTokenizer st; private String token; private String peekToken() { if (token == null) try { while (st == null || !st.hasMoreTokens()) { line = r.readLine(); if (line == null) return null; st = new StringTokenizer(line); } token = st.nextToken(); } catch (IOException e) { } return token; } private String nextToken() { String ans = peekToken(); token = null; return ans; } } public class bergur_kattio { static int MAXN = 100000; static par[][] a = new par[MAXN][2]; static int n; static int ss; static int[] b = new int[MAXN]; static int[] p = new int[MAXN]; static int[] s = new int[MAXN]; static int[] aa = new int[MAXN]; static Kattio sc = new Kattio(System.in); static int xx; static int yy; static void query(int x, int y) { System.out.println("? " + (x + 1) + " " + (y + 1)); xx = sc.getInt(); yy = sc.getInt(); } static void fix(int i, int x) { b[i] = x; } static boolean par_eq(par a, par b) { if (Math.min(a.x, a.y) != Math.min(b.x, b.y)) return false; if (Math.max(a.x, a.y) != Math.max(b.x, b.y)) return false; if (Math.min(a.z, a.w) != Math.min(b.z, b.w)) return false; if (Math.max(a.z, a.w) != Math.max(b.z, b.w)) return false; return true; } static void rem(par[] a, par p) { // removes p from a. a only has two elements. if (par_eq(a[0], p)) { par s = a[0]; a[0] = a[1]; a[1] = s; } } static void step1(par p1, par p2) { int x, y; if (p1.x == p2.y || p1.y == p2.x) { int s = p2.x; p2.x = p2.y; p2.y = s; } rem(a[p1.x], p1); aa[p1.x]--; rem(a[p1.y], p1); aa[p1.y]--; rem(a[p2.x], p2); aa[p2.x]--; rem(a[p2.y], p2); aa[p2.y]--; if (p1.x == p2.x && p1.y == p2.y) { aa[p1.x] = aa[p1.y] = 0; while (true) { query(p1.z, p2.z); x = xx; y = yy; if (x == y) { fix(p1.z, x); fix(p2.z, x); fix(p1.w, p1.x + p1.y - x); fix(p2.w, p1.x + p1.y - x); break; } query(p1.z, p1.w); x = xx; y = yy; if (x == y) { fix(p1.z, x); fix(p1.w, x); fix(p2.z, p1.x + p1.y - x); fix(p2.w, p1.x + p1.y - x); break; } } return; } if (p1.x == p2.y) { int s = p2.x; p2.x = p2.y; p2.y = s; } if (p1.y == p2.x) { int s = p1.x; p1.x = p1.y; p1.y = s; } if (p1.y == p2.y) { int s = p1.x; p1.x = p1.y; p1.y = s; s = p2.x; p2.x = p2.y; p2.y = s; } if (p1.x == p2.x) { query(p1.z, p2.z); x = xx; y = yy; if (x == y) { fix(p1.z, x); fix(p2.z, x); fix(p1.w, p1.x + p1.y - x); fix(p2.w, p2.x + p2.y - x); } else { if (x == p2.y || y == p1.y) { int s = x; x = y; y = s; } fix(p1.w, p1.x + p1.y - x); fix(p2.w, p2.x + p2.y - y); par pp = new par(x, y, p1.z, p2.z); a[x][aa[x]++] = pp; a[y][aa[y]++] = pp; } if (aa[p1.x] == 1) s[ss++] = p1.x; if (aa[p1.y] == 1) s[ss++] = p1.y; if (aa[p2.x] == 1) s[ss++] = p2.x; if (aa[p2.y] == 1) s[ss++] = p2.y; // new orphans. these might be fixed later incidentally. return; } return; } static par[] h = new par[MAXN]; static int hh = 0; static void step2(par p1, par p2) { int x, y; query(p1.z, p2.z); x = xx; y = yy; if (x != p1.x && x != p1.y) { int s = x; x = y; y = s; } fix(p1.w, p1.x + p1.y - x); fix(p2.w, p2.x + p2.y - y); h[hh++] = new par(x, y, p1.z, p2.z); return; } static void step3(par p) { int i, x, y; for (i = 0; i < n; i++) if (b[i] == p.x && i != p.z && i != p.w) break; while (true) { query(i, p.z); x = xx; y = yy; if (x == y) { fix(p.z, x); fix(p.w, p.x + p.y - x); break; } query(p.w, p.z); x = xx; y = yy; if (x == y) { fix(p.z, x); fix(p.w, x); fix(i, p.x + p.y - x); break; } } } public static void main(String[] arg) { int i, x, y; n = sc.getInt(); for (i = 0; i < n; i++) aa[i] = 0; for (i = 0; i < n; i++) p[i] = i; for (i = 0; i < n/2; i++) { query(p[i*2], p[2*i + 1]); x = xx; y = yy; if (x == y) { fix(p[2*i], x); fix(p[2*i + 1], y); } else { par pp = new par(x, y, p[2*i], p[2*i + 1]); a[x][aa[x]++] = pp; a[y][aa[y]++] = pp; } } for (i = 0; i < n; i++) { if (aa[i] == 0) continue; step1(a[i][0], a[i][1]); while (ss > 0) { x = s[--ss]; if (aa[x] == 0) continue; // already been deorphaned. y = a[x][0].x + a[x][0].y - x; if (aa[y] == 1) { h[hh++] = a[x][0]; aa[x] = aa[y] = 0; } else if (par_eq(a[x][0], a[y][0])) step1(a[x][0], a[y][1]); else if (par_eq(a[x][0], a[y][1])) step1(a[x][0], a[y][0]); } } while (hh > 1) { par p1 = h[--hh], p2 = h[--hh]; step2(p1, p2); } if (hh != 0) step3(h[0]); System.out.print("!"); for (i = 0; i < n; i++) System.out.print(" " + b[i]); System.out.println(""); } }