The only difference between this problem and the other two versions is the maximum number of queries. In this version, you are allowed to ask at most $ \mathbf{20} $ queries. You can make hacks only if all versions of the problem are solved.
This is an interactive problem.
“Everybody! Doremy’s Perfect Data Structure Class is about to start! Come and do your best if you want to have as much IQ as me!” In today’s Data Structure class, Doremy is teaching everyone a powerful data structure — Doremy tree! Now she gives you a quiz to prove that you are paying attention in class.
Given an array a a a of length m m m , Doremy tree supports the query Q ( l , r , k ) Q(l,r,k) Q(l,r,k) , where 1 ≤ l ≤ r ≤ m 1 \leq l \leq r \leq m 1≤l≤r≤m and 1 ≤ k ≤ m 1 \leq k \leq m 1≤k≤m , which returns the number of distinct integers in the array [ ⌊ a l k ⌋ , ⌊ a l + 1 k ⌋ , … , ⌊ a r k ⌋ ] \left[\lfloor\frac{a_l}{k} \rfloor, \lfloor\frac{a_{l+1}}{k} \rfloor, \ldots, \lfloor\frac{a_r}{k} \rfloor\right] [⌊kal⌋,⌊kal+1⌋,…,⌊kar⌋] .
Doremy has a secret permutation p p p of integers from 1 1 1 to n n n . You can make queries, in one query, you give 3 3 3 integers l , r , k l,r,k l,r,k ( 1 ≤ l ≤ r ≤ n 1 \leq l \leq r \leq n 1≤l≤r≤n , 1 ≤ k ≤ n 1 \leq k \leq n 1≤k≤n ) and receive the value of Q ( l , r , k ) Q(l,r,k) Q(l,r,k) for the array p p p . Can you find the index y y y ( 1 ≤ y ≤ n 1 \leq y \leq n 1≤y≤n ) such that p y = 1 p_y=1 py=1 in at most 20 \mathbf{20} 20 queries?
Note that the permutation p p p is fixed before any queries are made.
You begin the interaction by reading an integer n n n ( 3 ≤ n ≤ 1024 3 \le n \le 1024 3≤n≤1024 ) in the first line — the length of the permutation.
To make a query, you should output
in a separate line. After each query, you should read an integer x x x — the value of Q ( l , r , k ) Q(l,r,k) Q(l,r,k) for p p p . In this version of the problem, you can make at most 20 20 20 such queries.To give the answer, you should output
in a separate line, where p y = 1 p_y=1 py=1 .After printing a query or the answer, do not forget to output the end of line and flush the output. Otherwise, you will get Idleness limit exceeded. To do this, use:
Hacks Format
The first line of the hack contains an integer n n n ( 3 ≤ n ≤ 1024 3 \le n \le 1024 3≤n≤1024 ) — the length of the permutation.
The second line of the hack contains n n n distinct integers p 1 , p 2 , … , p n p_1,p_2,\ldots,p_n p1,p2,…,pn ( 1 ≤ p i ≤ n 1 \le p_i\le n 1≤pi≤n ) — the permutation.
5
2
2
1
3
? 1 3 4
? 3 5 3
? 3 4 5
? 3 5 2
! 4
The permutation in the example is [ 3 , 5 , 2 , 1 , 4 ] [3,5,2,1,4] [3,5,2,1,4] .
The input and output for example illustrate possible interaction on that test (empty lines are inserted only for clarity).
In this interaction process:
The correct answer is got after 4 4 4 queries, so this process will be judged correct.
考虑 1 1 1 和其他的数有什么不同点。我们令询问的 k = 2 k=2 k=2,那么只有 1 1 1 的值是 0 0 0,其余都不是。这看起来并没有什么用,因为我们只能知道不同的数的个数。
可以发现,若 k = 2 k=2 k=2,则所有数下取整后是两两配对的, ⌊ 2 2 ⌋ = ⌊ 3 2 ⌋ , ⌊ 4 2 ⌋ = ⌊ 5 2 ⌋ … \lfloor\frac{2}{2}\rfloor=\lfloor\frac{3}{2}\rfloor,\lfloor\frac{4}{2}\rfloor=\lfloor\frac{5}{2}\rfloor\dots ⌊22⌋=⌊23⌋,⌊24⌋=⌊25⌋…。当 n n n 为奇数时,只有 1 1 1 是单出来的; n n n 为偶数时, 1 1 1 和 n n n 两个数都是单出来的。 于是就可以分类讨论:
对于一个区间 [ l , r ] [l,r] [l,r],若 query ( l , r , 2 ) = x \operatorname{query}(l,r,2)=x query(l,r,2)=x,可以得到 [ l , r ] [l,r] [l,r] 中有 2 x − ( r − l + 1 ) 2x−(r−l+1) 2x−(r−l+1) 个数字没有被配对。所以假设我们找了一个分界点 m i d mid mid,并求出 [ 1 , m i d ] [1,mid] [1,mid] 中有 L L L 个没配对, [ m i d + 1 , n ] [mid+1,n] [mid+1,n] 中有 R R R 个没配对。那么如果同一组的数分到了一边一个,它们可以互相消掉,只有 1 是无论如何都无法被配对的。
这就是说,若 L < R L
我们同样询问得到 L L L 和 R R R 的值。分类讨论 1 1 1 和 n n n 的位置情况:
在一左一右的情况下,我们看似没法判断哪一边是 1 1 1,其实可以找出 n n n 的位置:令 k = n k=n k=n,这样只有 n n n 的答案是 1 1 1,其他都是 0 0 0。因此可以在一开始先二分找出 n n n 的位置,就能知道 L = R L=R L=R 时 1 1 1 在哪边了。
找 n n n 的位置只需要询问一边,所以总询问次数是 3 × log n ≤ 30 3\times\log{n}\le30 3×logn≤30。
发现找 1 1 1 的位置的过程是不好优化的,现在的询问次数难点在找 n n n 的位置。
实际上,我们只关心 n n n 在 m i d mid mid 的哪一侧,而不关心它的具体位置。可以这样优化:
因此,在第一次判断时记录 n n n 在 m i d mid mid 的哪一侧,以后不需要再次判断。总询问次数为 2 × log n + 1 ≤ 21 2\times\log{n}+1≤21 2×logn+1≤21。
还需要再卡掉一次询问,使询问次数到 20 20 20 以内。
考虑二分的终止状态,在 l + 1 = r l+1=r l+1=r 的情况下,把两边都问一遍看起来就不太划算,可以只问一边呢。
如果我们现在还没询问过 n n n 在 m i d mid mid 的哪一侧,说明 n n n 还在区间 [ l , r ] [l,r] [l,r] 中。换句话讲, l l l 和 r r r 的位置上一个是 n n n 一个是 1 1 1。
这种情况简单的:一次询问就能找出 n n n 的位置,剩下的那个自然是 1 1 1。
n n n 不在区间里,考虑我们已经知道了哪些信息。
在达成当前局面之前,我们曾令 m i d = l − 1 mid=l−1 mid=l−1,也曾令 m i d = r mid=r mid=r。所以可以知道 query ( 1 , l − 1 , 2 ) \operatorname{query}(1,l−1,2) query(1,l−1,2) 和 query ( r + 1 , n , 2 ) \operatorname{query}(r+1,n,2) query(r+1,n,2) 的答案。于是就有了如下操作:
在完成该操作后,最后一步二分被优化到一次询问,询问次数上界为 2 × log n ≤ 20 2\times\log{n}\le20 2×logn≤20, H a r d Hard Hard 版成功解决。
至此,艺术已成。 至此,艺术已成。 至此,艺术已成。
// C++ includes used for precompiling -*- C++ -*-
// Copyright (C) 2003-2013 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// .
/** @file stdc++.h
* This is an implementation file for a precompiled header.
*/
// 17.4.1.2 Headers
// C
#ifndef _GLIBCXX_NO_ASSERT
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if __cplusplus >= 201103L
#include
#include
#include
#include
#include
#include
#include
#include
#include
#endif
// C++
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if __cplusplus >= 201103L
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#endif
using namespace std;
#define int long long
int n;
map<pair<int, int>, int> Map;
int answer;
inline int Ask(int l, int r, int p) {
if (Map.count({l, r}) && p == 2) {
return Map[ {l, r}];
}
int x;
if (l == r) {
x = 1;
} else if (l == 1 && r == n) {
x = n / p + 1;
} else {
cout << "? " << l << " " << r << " " << p << endl;
cin >> x;
}
if (p == 2) {
Map[ {l, r}] = x;
}
return x;
}
inline int choose_project(int n) {
return n & 1 ? 1 : 2;
}
inline int binery_sreach_to_project_1() {
int answer;
int l, r, mid;
l = 1, r = n;
while (l <= r) {
int mid = (l + r) >> 1;
if (mid == n) {
answer = n;
break;
}
int val_l = Ask(1, mid, 2);
val_l = 2 * val_l - mid;
int val_r = Ask(mid + 1, n, 2);
val_r = 2 * val_r - (n - mid);
if (val_l < val_r) {
l = mid + 1;
} else {
r = mid - 1;
answer = mid;
}
}
return answer;
}
inline int binery_sreach_to_project_2() {
int answer;
int l, r, mid;
l = 1, r = n;
int tmp = -1;
while (l <= r) {
int mid = (l + r) >> 1;
if (mid == n) {
answer = n;
break;
}
if (l == r && l != 1) {
if (tmp == -1) {
if (Ask(1, l, n) == 1) {
answer = mid;
}
break;
}
int val1 = Ask(l, n, 2), val2 = Ask(l + 2, n, 2);
if (val1 - val2 == 0) {
break;
} else if (val1 - val2 == 1) {
if (Ask(1, l + 1, 2) - Ask(1, l - 1, 2) == 2 && Ask(l + 1, n, 2) == Ask(l + 2, n, 2)) {
answer = mid;
}
} else {
if (Ask(1, l - 1, 2) != Ask(1, l, 2)) {
answer = mid;
}
}
break;
}
int val_l = Ask(1, mid, 2);
val_l = 2 * val_l - mid - (1 <= tmp && tmp <= mid);
int val_r = Ask(mid + 1, n, 2);
val_r = 2 * val_r - (n - mid) - (mid + 1 <= tmp && tmp <= n);
if (val_l > val_r) {
r = mid - 1;
answer = mid;
} else if (val_l < val_r) {
l = mid + 1;
} else {
if (mid == 1) {
if (Ask(mid + 1, n, n) == 2) {
tmp = mid + 1;
answer = mid;
r = mid - 1;
} else {
l = mid + 1;
tmp = 1;
}
} else {
if (Ask(1, mid, n) == 2) {
l = mid + 1;
tmp = 1;
} else {
answer = mid;
tmp = mid + 1;
r = mid - 1;
}
}
}
}
return answer;
}
inline void work() {
int projectnum;
cin >> n;
projectnum = choose_project(n);
if (projectnum == 1) {
answer = binery_sreach_to_project_1();
} else if (projectnum == 2) {
answer = binery_sreach_to_project_2();
}
cout << "! " << answer;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
work();
return 0;
}