結果
| 問題 |
No.3018 目隠し宝探し
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2025-07-02 00:55:51 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
WA
|
| 実行時間 | - |
| コード長 | 9,024 bytes |
| コンパイル時間 | 1,196 ms |
| コンパイル使用メモリ | 90,888 KB |
| 実行使用メモリ | 26,320 KB |
| 平均クエリ数 | 2.68 |
| 最終ジャッジ日時 | 2025-07-02 00:55:56 |
| 合計ジャッジ時間 | 4,073 ms |
|
ジャッジサーバーID (参考情報) |
judge3 / judge5 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 1 |
| other | AC * 15 WA * 6 |
コンパイルメッセージ
main.cpp: In function ‘int main()’:
main.cpp:242:37: warning: ‘final_i’ may be used uninitialized [-Wmaybe-uninitialized]
242 | std::cout << "! " << final_i << " " << final_j << std::endl;
| ^~~
main.cpp:31:9: note: ‘final_i’ was declared here
31 | int final_i, final_j;
| ^~~~~~~
main.cpp:242:44: warning: ‘final_j’ may be used uninitialized [-Wmaybe-uninitialized]
242 | std::cout << "! " << final_i << " " << final_j << std::endl;
| ^~~~~~~
main.cpp:31:18: note: ‘final_j’ was declared here
31 | int final_i, final_j;
| ^~~~~~~
ソースコード
#include <iostream>
#include <string>
#include <cmath>
#include <algorithm> // for std::min and std::max
// 標準出力をフラッシュするヘルパー関数
void flush_stdout() {
std::cout << std::flush;
}
int main() {
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
int H, W;
std::cin >> H >> W;
long long d1, d2;
// 1回目の質問: マス (1, 1)
std::cout << "? 1 1" << std::endl;
flush_stdout();
std::cin >> d1;
if (d1 == -1) {
return 0; // エラー終了
}
// H=1 の場合、行は確定 (i=1)
// W=1 の場合、列は確定 (j=1)
int final_i, final_j;
if (H == 1 && W == 1) {
final_i = 1;
final_j = 1;
} else if (H == 1) { // H=1の場合、iは確定
final_i = 1;
// j を特定するために、マス (1, W) を質問
std::cout << "? 1 " << W << std::endl;
flush_stdout();
std::cin >> d2;
if (d2 == -1) {
return 0; // エラー終了
}
// d1 = (1-1)^2 + (j-1)^2 = (j-1)^2
// d2 = (1-1)^2 + (j-W)^2 = (j-W)^2
// (j-1)^2 = d1
// (j-W)^2 = d2
// (j-1)^2 - (j-W)^2 = d1 - d2
// (j^2 - 2j + 1) - (j^2 - 2Wj + W^2) = d1 - d2
// -2j + 1 + 2Wj - W^2 = d1 - d2
// 2j(W-1) = d1 - d2 - 1 + W^2
// j = (d1 - d2 - 1 + W*W) / (2 * (W-1)) + 1
// よりシンプルな導出:
// (j-1)^2 = d1
// j-1 = sqrt(d1) (j-1 >= 0)
// j = 1 + sqrt(d1)
// または
// j-1 = -sqrt(d1) (j-1 < 0)
// j = 1 - sqrt(d1)
// 適応的ジャッジのため、sqrt(d1)はj-1か-(j-1)のどちらかになる
// (j-1)^2 = d1
// (j-W)^2 = d2
// この2つからjを求める
// j^2 - 2j + 1 = d1
// j^2 - 2Wj + W^2 = d2
// 上の式から下の式を引くと
// (-2j + 2Wj) + (1 - W^2) = d1 - d2
// 2j(W-1) = d1 - d2 - 1 + W^2
// j = (d1 - d2 - 1 + W*W) / (2.0 * (W-1)) + 1
// この計算は浮動小数点になるため、整数で扱う場合は注意が必要
// d1 - (j-1)^2 = 0
// d2 - (j-W)^2 = 0
// d1 + 2Wj - W^2 = d2 + 2j - 1 (j^2項が消える)
// 2j(W-1) = d2 - d1 - 1 + W^2
// j = (d2 - d1 - 1 + W*W) / (2 * (W-1)) + 1
// W=1ではないので、W-1 > 0
// d1は(j-1)^2
// d2は(j-W)^2
// jを特定するために、もう一つ点を質問する必要はない
// d1からjの候補を絞り、d2からその候補を検証
// sqrt(d1) を s1とする。 j = 1 + s1 もしくは j = 1 - s1
// sqrt(d2) を s2とする。 j = W + s2 もしくは j = W - s2
// (j-1)^2 = d1 より j-1 = k (k=sqrt(d1) or -sqrt(d1))
// (j-W)^2 = d2 より j-W = m (m=sqrt(d2) or -sqrt(d2))
// j = 1 + k
// j = W + m
// 1 + k = W + m => k - m = W - 1
// kとmはそれぞれsqrt(d1)とsqrt(d2)の正負どちらか
// 4通りの組み合わせを試して、一致するjを探す
for (long long k_val : {static_cast<long long>(std::sqrt(d1)), -static_cast<long long>(std::sqrt(d1))}) {
if ((k_val * k_val) != d1) continue; // k_valが整数にキャストされた結果なので元のd1と一致するか確認
long long current_j = 1 + k_val;
if (current_j >= 1 && current_j <= W) {
if (((current_j - W) * (current_j - W)) == d2) {
final_j = static_cast<int>(current_j);
break;
}
}
}
} else if (W == 1) { // W=1の場合、jは確定
final_j = 1;
// i を特定するために、マス (H, 1) を質問
std::cout << "? " << H << " 1" << std::endl;
flush_stdout();
std::cin >> d2;
if (d2 == -1) {
return 0; // エラー終了
}
// d1 = (i-1)^2 + (1-1)^2 = (i-1)^2
// d2 = (i-H)^2 + (1-1)^2 = (i-H)^2
// 上と同様にiを特定
for (long long k_val : {static_cast<long long>(std::sqrt(d1)), -static_cast<long long>(std::sqrt(d1))}) {
if ((k_val * k_val) != d1) continue;
long long current_i = 1 + k_val;
if (current_i >= 1 && current_i <= H) {
if (((current_i - H) * (current_i - H)) == d2) {
final_i = static_cast<int>(current_i);
break;
}
}
}
} else { // H > 1 かつ W > 1 の場合
// マス (1, W) を質問して d2 を得る
// これにより、(i-1)^2 と (j-W)^2 の情報が得られる
std::cout << "? 1 " << W << std::endl;
flush_stdout();
std::cin >> d2;
if (d2 == -1) {
return 0; // エラー終了
}
// d1 = (i-1)^2 + (j-1)^2
// d2 = (i-1)^2 + (j-W)^2
// d1 - d2 = (j-1)^2 - (j-W)^2
// d1 - d2 = (j^2 - 2j + 1) - (j^2 - 2Wj + W^2)
// d1 - d2 = -2j + 1 + 2Wj - W^2
// d1 - d2 = 2j(W-1) + 1 - W^2
// 2j(W-1) = d1 - d2 - 1 + W^2
// j = (d1 - d2 - 1 + W*W) / (2.0 * (W-1)) + 1
// 浮動小数点誤差を避けるため、整数で計算
// (j-1)^2 は d1 - (i-1)^2
// (j-W)^2 は d2 - (i-1)^2
// よって、d1 - (i-1)^2 と d2 - (i-1)^2 の差が一定
// この差を利用してjを決定します。
// sqrt(d1)とsqrt(d2)を考慮して、jを特定
// (i-1)^2 = A
// (j-1)^2 = B
// (j-W)^2 = C
// A + B = d1
// A + C = d2
// C - B = d2 - d1
// (j-W)^2 - (j-1)^2 = d2 - d1
// j^2 - 2Wj + W^2 - (j^2 - 2j + 1) = d2 - d1
// -2Wj + W^2 + 2j - 1 = d2 - d1
// 2j(1-W) + W^2 - 1 = d2 - d1
// 2j(1-W) = d2 - d1 - W^2 + 1
// j = (d2 - d1 - W*W + 1) / (2.0 * (1-W))
// これでjが求まります。
// 適応的ジャッジのため、複数の候補がある場合でも、
// 1つの質問でi座標の候補が2つ、もう1つの質問でj座標の候補が2つ
// 合計4つの交点候補がある可能性がある。
// しかし、実際には2つの質問で一意に定まることが多い。
// 確実な方法: 2点からの距離で円の方程式を立て、交点を求める
// 宝物の座標を (r, c) とする
// (r-1)^2 + (c-1)^2 = d1
// (r-1)^2 + (c-W)^2 = d2
// 上記2式から (r-1)^2 を消去
// d1 - (c-1)^2 = d2 - (c-W)^2
// d1 - (c^2 - 2c + 1) = d2 - (c^2 - 2Wc + W^2)
// d1 - c^2 + 2c - 1 = d2 - c^2 + 2Wc - W^2
// 2c - 2Wc = d2 - d1 - W^2 + 1
// 2c(1 - W) = d2 - d1 - W^2 + 1
// c = (d2 - d1 - W*W + 1) / (2.0 * (1 - W))
// if (1 - W == 0) (i.e. W == 1) then this formula is invalid
// W != 1 の場合、上記の式で c が一意に定まる。
// H != 1 の場合、r を求めるために同様に (1,1) と (H,1) で質問が必要?
// いいえ、2回で特定する必要がある。
// d1: (r-1)^2 + (c-1)^2
// d2: (r-1)^2 + (c-W)^2
// ここで r と c を直接計算する
// 実際には2つの質問で (i-1)^2 と (j-1)^2 を特定する。
// d1 = (i-1)^2 + (j-1)^2
// d2 = (i-1)^2 + (j-W)^2
// x = i-1
// y = j-1
// z = j-W
// x^2 + y^2 = d1
// x^2 + z^2 = d2
// y = j-1
// z = j-W = (j-1) - (W-1) = y - (W-1)
// x^2 + y^2 = d1
// x^2 + (y - (W-1))^2 = d2
// (y - (W-1))^2 = d2 - x^2
// y^2 - 2y(W-1) + (W-1)^2 = d2 - x^2
// d1 - x^2 - 2y(W-1) + (W-1)^2 = d2 - x^2
// d1 - 2y(W-1) + (W-1)^2 = d2
// 2y(W-1) = d1 + (W-1)^2 - d2
// y = (d1 + (W-1)*(W-1) - d2) / (2.0 * (W-1))
// もし W-1 が 0 なら (W=1の場合)、この式は使えない。
// 上で W=1 の場合はすでに処理済み。
long long y_val_times_2_W_minus_1 = d1 + (long long)(W-1)*(W-1) - d2;
long long divisor_for_y = 2 * (W-1);
// yが整数であることを確認し、除算
// y = j-1
final_j = 1 + static_cast<int>(y_val_times_2_W_minus_1 / divisor_for_y);
// x^2 = d1 - y^2
// x^2 = d1 - (final_j - 1)^2
long long x_squared = d1 - (long long)(final_j - 1)*(final_j - 1);
final_i = 1 + static_cast<int>(std::sqrt(x_squared));
// x = i-1 なので、i = 1 + x
// xは正の値を取るようにすることで、1 <= i <= H の範囲で妥当な値になるはず。
// 適応的ジャッジのため、sqrt(x_squared)は適切な値になる
}
// 解答を出力
std::cout << "! " << final_i << " " << final_j << std::endl;
flush_stdout();
return 0;
}