description: It turns out that the meaning of life is a permutation p_1, p_2, …, p_n of the integers 1, 2, …, n (2 ≤ n ≤ 100). Omkar, having created all life, knows this permutation, and will allow you to figure it out using some queries.
  
  A query consists of an array a_1, a_2, …, a_n of integers between 1 and n. a is not required to be a permutation. Omkar will first compute the pairwise sum of a and p, meaning that he will compute an array s where s_j = p_j + a_j for all j = 1, 2, …, n. Then, he will find the smallest index k such that s_k occurs more than once in s, and answer with k. If there is no such index k, then he will answer with 0.
  
  You can perform at most 2n queries. Figure out the meaning of life p.
  
  Interaction
  
  Start the interaction by reading single integer n (2 ≤ n ≤ 100) — the length of the permutation p.
  
  You can then make queries. A query consists of a single line "? \enspace a_1 \enspace a_2 \enspace … \enspace a_n" (1 ≤ a_j ≤ n).
  
  The answer to each query will be a single integer k as described above (0 ≤ k ≤ n).
  
  After making a query do not forget to output end of line and flush the output. Otherwise, you will get Idleness limit exceeded. To do this, use:
  
    * fflush(stdout) or cout.flush() in C++;
    * System.out.flush() in Java;
    * flush(output) in Pascal;
    * stdout.flush() in Python;
    * see documentation for other languages.
  
  
  
  To output your answer, print a single line "! \enspace p_1 \enspace p_2 \enspace … \enspace p_n" then terminate.
  
  You can make at most 2n queries. Outputting the answer does not count as a query.
  
  Hack Format
  
  To hack, first output a line containing n (2 ≤ n ≤ 100), then output another line containing the hidden permutation p_1, p_2, …, p_n of numbers from 1 to n.
  
  Example
  
  Input
  
  
  5
  
  2
  
  0
  
  1
  
  
  Output
  
  
  
  ? 4 4 2 3 2
  
  ? 3 5 1 5 5
  
  ? 5 2 4 3 1
  
  ! 3 2 1 5 4
  
  Note
  
  In the sample, the hidden permutation p is [3, 2, 1, 5, 4]. Three queries were made.
  
  The first query is a = [4, 4, 2, 3, 2]. This yields s = [3 + 4, 2 + 4, 1 + 2, 5 + 3, 4 + 2] = [7, 6, 3, 8, 6]. 6 is the only number that appears more than once, and it appears first at index 2, making the answer to the query 2.
  
  The second query is a = [3, 5, 1, 5, 5]. This yields s = [3 + 3, 2 + 5, 1 + 1, 5 + 5, 4 + 5] = [6, 7, 2, 10, 9]. There are no numbers that appear more than once here, so the answer to the query is 0.
  
  The third query is a = [5, 2, 4, 3, 1]. This yields s = [3 + 5, 2 + 2, 1 + 4, 5 + 3, 4 + 1] = [8, 4, 5, 8, 5]. 5 and 8 both occur more than once here. 5 first appears at index 3, while 8 first appears at index 1, and 1 < 3, making the answer to the query 1.
  
  Note that the sample is only meant to provide an example of how the interaction works; it is not guaranteed that the above queries represent a correct strategy with which to determine the answer.
solution: import java.io.*;
  import java.util.*;
  /*
      -->Since, all the numbers in the final permutation is [1,n]
       -->For every index, we may find the index of next larger or smaller index(iff the next smaller/larger element is on left).
      --> Otherwise if the next smaller or larger is on right, we get current index as answer from OJ, and it will not be helpful.
      --> if we print 2 in case of current index and rest 3 for other index , the answer we get may get index of next smaller element of element at index i..
      --> Similarly if we print 2 in case of current index and rest 1 for other index, the answer we get may contain index of next larger element of element at index i.
      --> Note, in case of element 1, we get 0 while finding next smaller element
          in case of element n, we get 0 while finding next greater element.
     --> if we are at current index i and we see larger[i] !=i and we know the value of element at index larger[i],
          then current element is (element at index larger[i]) - 1
     --> if we are at current index i and we see smaller[i] != i and we know the value of element at index smaller[i]
          then current element is (element at index smaller[i]) + 1
     --> Doing only this will not be sufficient as we can have some indces whose smaller and greater both lies on right
             for them, we do a dfs from know indeces, and we find their values as well.
  
  
   */
  public class CodeForces{
      /*-------------------------------------------EDITING CODE STARTS HERE-------------------------------------------*/
      public static void main(String[] args) throws IOException{
          openIO();
          int testCase = 1;
  //        testCase = sc.nextInt();
          preCompute();
          for (int i = 1; i <= testCase; i++) solve(i);
          closeIO();
      }
  
      public static void solve(int tCase)throws IOException {
          int n = sc.nextInt();
          // since query element <= n, in case n=2, handle it explicitly
          if(n==2){
              out.println("? 2 1");
              out.flush();
              int x = sc.nextInt();
              if(x>0)out.println("! 1 2");
              else out.println("! 2 1");
              return;
          }
          int[] smaller = new int[n+1];
          int[] larger = new int[n+1];
          int[] ans = new int[n+1];
          for(int i=1;i<=n;i++){
              StringBuilder sb = new StringBuilder("? ");
              for(int j=1;j<=n;j++)
                  if(i==j)sb.append(2).append(" ");
                  else sb.append(3).append(" ");
              out.println(sb);
              out.flush();
              smaller[i] = sc.nextInt();
              if(smaller[i]==0) ans[i] = 1;
              sb = new StringBuilder("? ");
              for(int j=1;j<=n;j++)
                  if(i==j)sb.append(2).append(" ");
                  else sb.append(1).append(" ");
              out.println(sb);
              out.flush();
              larger[i] = sc.nextInt();
              if(larger[i]==0)ans[i] = n;
          }
          while (true) {
              boolean isOK = true;
              for(int i=1;i<=n;i++)
                  isOK &= ans[i]>0;
              if(isOK)break;
              for (int i = 1; i <= n; i++) {
                  if(ans[i]>0) dfs(i,ans,larger,smaller);
                  else if (ans[smaller[i]] != 0) {
                      ans[i] = ans[smaller[i]] + 1;
                      dfs(i,ans,larger,smaller);
                  }
                  else if (ans[larger[i]] != 0) {
                      ans[i] = ans[larger[i]] - 1;
                      dfs(i,ans,larger,smaller);
                  }
              }
          }
          out.print("! ");
          out.flush();
          for(int i=1;i<=n;i++)
              out.print(ans[i]+" ");
      }
  
      private static void dfs(int currIndex,int[] ans,int[] larger,int[] smaller){
          if(larger[currIndex] != currIndex){
              ans[larger[currIndex]] = 1 + ans[currIndex];
              dfs(larger[currIndex],ans,larger,smaller);
          }
          if(smaller[currIndex]!= currIndex){
              ans[smaller[currIndex]] = -1 + ans[currIndex];
              dfs(smaller[currIndex],ans,larger,smaller);
          }
      }
  
      private static void  preCompute(){
  
      }
      /*-------------------------------------------EDITING CODE ENDS HERE-------------------------------------------*/
      static FastestReader sc;
      static PrintWriter out;
      private static void openIO() throws IOException{
          sc = new FastestReader();
          out = new PrintWriter(System.out);
      }
  
      /*------------------------------------------HELPER FUNCTION STARTS HERE------------------------------------------*/
  
      public static final int mod = (int) 1e9 +7;
      private static final int mod2 =  998244353;
      public static final int inf_int = (int) 2e9;
      public static  final long inf_long = (long) 2e18;
  
      // euclidean algorithm time O(max (loga ,logb))
      public static long _gcd(long a, long b) {
          long x = Math.min(a, b);
          long y = Math.max(a, b);
          if (y % x == 0) return x;
          return _gcd(y % x, x);
      }
  
      public static long _lcm(long a, long b) {
          // lcm(a,b) * gcd(a,b) = a * b
          return (a / _gcd(a, b)) * b;
      }
      // binary exponentiation time O(logn)
      public static long _power(long x, long n) {
          long ans = 1;
          while (n > 0) {
              if ((n & 1) == 1) {
                  ans *= x;
                  ans %= mod;
                  n--;
              } else {
                  x *= x;
                  x %= mod;
                  n >>= 1;
              }
          }
          return ans;
      }
      //sieve/first divisor time : O(mx * log ( log (mx) ) )
      public static int[]  _seive(int mx){
          int[] firstDivisor = new int[mx+1];
          for(int i=0;i<=mx;i++)firstDivisor[i] = i;
          for(int i=2;i*i<=mx;i++)
              if(firstDivisor[i] == i)
                  for(int j = i*i;j<=mx;j+=i)
                      firstDivisor[j] = i;
          return firstDivisor;
      }
  
      private static boolean isPrime(long x){
          for(long i=2;i*i<=x;i++)
              if(x%i==0)return false;
          return true;
      }
  
  
      /*------------------------------------------HELPER FUNCTION ENDS HERE-------------------------------------------*/
  
      /*-------------------------------------------FAST INPUT STARTS HERE---------------------------------------------*/
  
      public static void closeIO() throws IOException{
          out.flush();
          out.close();
          sc.close();
      }
      private static final class FastestReader {
          private static final int BUFFER_SIZE = 1 << 16;
          private final DataInputStream din;
          private final byte[] buffer;
          private int bufferPointer, bytesRead;
  
          public FastestReader() {
              din = new DataInputStream(System.in);
              buffer = new byte[BUFFER_SIZE];
              bufferPointer = bytesRead = 0;
          }
  
          public FastestReader(String file_name) throws IOException {
              din = new DataInputStream(new FileInputStream(file_name));
              buffer = new byte[BUFFER_SIZE];
              bufferPointer = bytesRead = 0;
          }
  
          private static boolean isSpaceChar(int c) { return !(c >= 33 && c <= 126); }
  
          private int skip() throws IOException {
              int b;
              //noinspection StatementWithEmptyBody
              while ((b = read()) != -1 && isSpaceChar(b)) {}
              return b;
          }
  
          public String next() throws IOException {
              int b = skip();
              final StringBuilder sb = new StringBuilder();
              while (!isSpaceChar(b)) { // when nextLine, (isSpaceChar(b) && b != ' ')
                  sb.appendCodePoint(b);
                  b = read();
              }
              return sb.toString();
          }
  
          public int nextInt() throws IOException {
              int ret = 0;
              byte c = read();
              while (c <= ' ') {
                  c = read();
              }
              final boolean neg = c == '-';
              if (neg) { c = read(); }
              do {
                  ret = ret * 10 + c - '0';
              } while ((c = read()) >= '0' && c <= '9');
  
              if (neg) { return -ret; }
              return ret;
          }
  
          public long nextLong() throws IOException {
              long ret = 0;
              byte c = read();
              while (c <= ' ') { c = read(); }
              final boolean neg = c == '-';
              if (neg) { c = read(); }
              do {
                  ret = ret * 10 + c - '0';
              } while ((c = read()) >= '0' && c <= '9');
              if (neg) { return -ret; }
              return ret;
          }
  
          public double nextDouble() throws IOException {
              double ret = 0, div = 1;
              byte c = read();
              while (c <= ' ') { c = read(); }
              final boolean neg = c == '-';
              if (neg) { c = read(); }
  
              do {
                  ret = ret * 10 + c - '0';
              } while ((c = read()) >= '0' && c <= '9');
  
              if (c == '.') {
                  while ((c = read()) >= '0' && c <= '9') {
                      ret += (c - '0') / (div *= 10);
                  }
              }
  
              if (neg) { return -ret; }
              return ret;
          }
  
          private void fillBuffer() throws IOException {
              bytesRead = din.read(buffer, bufferPointer = 0, BUFFER_SIZE);
              if (bytesRead == -1) { buffer[0] = -1; }
          }
  
          private byte read() throws IOException {
              if (bufferPointer == bytesRead) { fillBuffer(); }
              return buffer[bufferPointer++];
          }
  
          public void close() throws IOException {
              din.close();
          }
      }
      /*---------------------------------------------FAST INPUT ENDS HERE ---------------------------------------------*/
  }