E - Last Rook Editorial by en_translator
Some of you may wondered what kind of query you should ask, because the chess board is in a two-dimensional plane.
For simplicity, let us first consider a one-dimensional case. That is, consider the following problem:
We have a sequence \((A_1, A_2, \dots, A_N)\). \(N-1\) of the \(N\) elements in \(A\) are \(1\), and the other one is \(0\). Repeat the following question to determine \(i\) such that \(A_i = 0\).
- Choose \(P\) and \(Q\). Ask how many \(1\)’s are there in \(A_P, A_{P+1}, \dots, A_{Q}\).
This problem can be solved with binary search through at most \(\lceil \log_2 N \rceil\) questions.
Specifically, perform the following steps:
- First, let \(L = 1, R = N\). \(L\) and \(R\) means that the answer is contained in \(\lbrack L, R \rbrack\).
- While \(L \neq R\), make the following query:
- Let \(M = \lfloor (L + R) / 2 \rfloor\), and ask a query with \((P, Q) = (L, M)\). The possible response to the query is either \(M-L+1\) or \(M-L\); we perform differently depending on this value:
- If the answer to the query is $M-L$, it means that $\lbrack L, M \rbrack$ contains $0$, so let $R \gets M$. - If the answer to the query is $M-L+1$, it maens that $\lbrack L, M \rbrack$ does not contain $0$, so let $L \gets M + 1$.
- Let \(M = \lfloor (L + R) / 2 \rfloor\), and ask a query with \((P, Q) = (L, M)\). The possible response to the query is either \(M-L+1\) or \(M-L\); we perform differently depending on this value:
Since the inspected segment is halved in each iteration, we can find the answer through \(\lceil \log_2 N \rceil\) questions.
In fact, the algorithm can be adopted to two-dimensional cases.
Let us divide the problem. Since there are only one possible square, we can solve the problem if we know the following two:
- Which column does not contain a rook?
- Which row does not contain a rook?
Consider finding these answers separately.
First, let us fix \((C, D)\) to \((1, N)\). Then the query is against the region from \(1\)-st through \(N\)-th column, that is, the entire row for each fixed row.
Then the contribution to the answer of each column is \(1\) if it has a rook, or \(0\) otherwise. Thus, it is boiled down to the same situation for the one-dimensional case. That is, we can apply the algorithm above, regarding \(A\) and \(B\) as \(L\) and \(R\), so that we can determine “which row does not contain a rook” through \(\lceil \log_2 N \rceil\) questions. Similarly, we can fix \((A, B)\) to \((1, N)\) to find “which column does not contain a rook?”
With these two pieces of information, we can find which square to place a rook. The maximum number of questions required is \(\lceil \log_2 N \rceil \times 2 \leq 10 \times 2 = 20\) times, which exactly fits in the limit of the number of queries. Also, the complexity is \(\mathrm{O}(\log N)\), which is fast enough. Therefore, the problem has been solved.
The following is a sample code in C++. Here are some tips:
- In C++, by appending
endl
to the output, it is flushed right after creating a new line. (When writing an interactive code in C++, all you have to care is thisendl
.) However, if you replaceendl
with"\n"
with a macro, it may not be flushed, so be careful if you are using your original macros.- However, strictly speaking, the sample code still works and gets AC (accepted) even if
endl
is replaced with"\n"
. If you are curious, go ahead and check the specifications of C++.
- However, strictly speaking, the sample code still works and gets AC (accepted) even if
- As in the
send
function in the 4-th line, it is useful to define a function that receives the output to the judge program as arguments and the values passed from the judge as the return values, in order to reducecin
,cout
, etc. and clarify the implementation. - As in the sample code, it may be useful to manage the segment of the binary search as a half-open segment \([1, N+1)\) to reduce the conditional branches and help implementation easier. (It may depend on preference.)
#include <iostream>
using namespace std;
int send(int A, int B, int C, int D) {
cout << "? " << A << " " << B << " " << C << " " << D << endl;
cin >> A;
return A;
}
int main() {
int N;
cin >> N;
int U = 1, D = N + 1;
while (U + 1 != D) {
int M = (U + D) / 2;
int c = send(U, M - 1, 1, N);
(c == M - U ? U : D) = M;
}
int L = 1, R = N + 1;
while (L + 1 != R) {
int M = (L + R) / 2;
int c = send(1, N, L, M - 1);
(c == M - L ? L : R) = M;
}
cout << "! " << U << " " << L << endl;
}
posted:
last update: