use std::io::Write; use std::collections::*; type Map = BTreeMap; type Set = BTreeSet; type Deque = VecDeque; fn main() { let n = read(); let mut memo = vec![]; let mut g = 0; for i in 0..(n - 1) { println!("? {} {}", i, n - 1); let v = read(); memo.push(v); g = gcd(g, v); } if g == 0 { println!("! -1"); return; } let mut cond = (1..=9).filter(|k| g % k == 0 && memo.iter().all(|m| *m / *k <= 9)).collect::>(); let x = (0..memo.len()).filter(|x| memo[*x] != 0).collect::>(); if cond.len() > 1 { if x.len() < 2 { println!("! -1"); return; } let pos = [0, x[0], x[1]]; println!("? {} {}", x[0], x[1]); let v = read(); let val = [memo[pos[0]], v, memo[pos[1]]]; let mut ncond = vec![]; for mut v in cond { let ini = v; let mut ok = true; for &val in val.iter() { ok &= v > 0 && val % v == 0 && val / v <= 9; if ok { v = val / v; } } if v == ini { ncond.push(v); } } cond = ncond; } assert!(cond.len() == 1); let mut ans = vec![]; let s = cond[0]; ans.push(s); for m in memo.iter() { ans.push(*m / s); } use util::*; println!("! {}", ans.iter().join("")); } fn gcd(a: usize, b: usize) -> usize { if b == 0 { a } else { gcd(b, a % b) } } fn read() -> usize { let mut s = String::new(); std::io::stdin().read_line(&mut s).unwrap(); s.trim().parse().unwrap() } mod util { pub trait Join { fn join(self, sep: &str) -> String; } impl Join for I where I: Iterator, T: std::fmt::Display, { fn join(self, sep: &str) -> String { let mut s = String::new(); use std::fmt::*; for (i, v) in self.enumerate() { if i > 0 { write!(&mut s, "{}", sep).ok(); } write!(&mut s, "{}", v).ok(); } s } } }