题目描述
难度分:2000
输入n(3≤n≤1000)。
评测机有一个在[1,n−1]中的整数x,你需要猜出x是多少。
你可以执行如下询问,至多15次:
+ c
:让评测机把x永久增加c(1≤c≤n−1),然后评测机返回给你⌊xn⌋。
如果你猜出了当前的x是多少,那么立刻输出! x
。
注意:你需要输出的是x增加之后的值,不是初始值。
输入样例1
3
1
输出样例1
+ 1
! 3
输入样例2
5
0
0
1
输出样例2
+ 1
+ 1
+ 1
! 5
输入样例3
10
0
0
1
2
输出样例3
+ 2
+ 2
+ 3
+ 8
! 20
算法
二分答案
这个搜索次数的限制,比较容易想到二分答案,但由于x在不断变化,check会比较难思考。我们可以二分最初的那个x=x0。并记录下整个搜索过程中累加的总数tot,那么最终的答案就应该是x=x0+tot。
记上一次⌊xn⌋的值为last,构造出一个可以产生单调性的增量。可以每次让tot增加c=n−((tot+mid)%n),即尽可能把当前的x往其上面的第一个n的倍数去凑。如果此时x=⌊x+cn⌋的值>last,就抛弃左边一半区间(但有可能是答案),否则抛弃右边一半区间。
最后的答案x就是二分出来的x0加上这个过程中的tot。
复杂度分析
时间复杂度
比较直观,就是在[1,n−1]上进行二分。二分答案的时间复杂度为O(log2n)。
空间复杂度
整个过程仅使用了有限几个变量,额外空间复杂度为O(1)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
scanf("%d", &n);
int l = 1, r = n - 1, tot = 0, last = 0;
while(l < r){
int mid = l + r + 1 >> 1;
int c = n - (tot + mid) % n;
printf("+ %d\n", c);
fflush(stdout);
int res;
scanf("%d", &res);
if(res > last) {
l = mid;
}else {
r = mid - 1;
}
tot += c;
last = res;
}
printf("! %d\n", tot + l);
fflush(stdout);
return 0;
}