import std; void main () { int H, W; readln.read(H, W); // 2乗のコストというのが大事で、実は広くてもクエリ3回でできる。 // -> 式展開をすると積に分離できて、2回でできる。(ヤバいね) int ask (int i, int j) { writefln("? %s %s", i, j); stdout.flush; int res = readln.chomp.to!int; enforce(0 <= res); return res; } void ans (int i, int j) { writefln("! %s %s", i, j); } if (H == 1 && W == 1) { ans(H, W); return; } if (H == 1) { int u = ask(1, 1); u--; foreach (i; -1 .. W + 1) { if (u == i * (i + 2)) { ans(1, i + 2); return; } } return; } if (W == 1) { int u = ask(1, 1); u--; foreach (i; -1 .. H + 1) { if (u == i * (i + 2)) { ans(i + 2, 1); return; } } return; } int p = ask(1, 1); if (p == 0) { ans(1, 1); return; } int q = ask(2, 1); if (q == 0) { ans(0, 0); return; } // h特定 -> w特定とすればクエリ2回 int h = -(q - p - 2 - 1) / 2; int w; int iscore = (h - 1) * (h - 1); foreach (i; 1 .. W + 1) { if (p - iscore - 1 == i * (i + 2)) { w = i + 2; break; } } ans(h, w); } void read (T...) (string S, ref T args) { import std.conv : to; import std.array : split; auto buf = S.split; foreach (i, ref arg; args) { arg = buf[i].to!(typeof(arg)); } }