有趣的交互题

我们考虑一件事情

如果我们询问的矩形中有一个端点

那么答案 $\mod 2 = 1$

否则答案 $\mod 2 = 0$

换句话说,就是如果询问到的答案$\mod 2 = 0$,那么这个矩形内要么没有端点,要么有两个端点

我们考虑

如果两个答案不在同一行

那么我们可以$O(n)$次询问找到这两行然后二分答案,这是非常简单易懂轻松愉快的

如果在同一行

那么他们肯定不在同一列

然后我们只需要找到他们中的一个,而且只需要二分一次就行了

因为两个在同一行,他们有一维坐标是一样的,如果二分两次的话被卡满就是$n + n + 2\log n = 2020$次询问,刚刚好爆掉(来自出题人的恶意)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include<bits/stdc++.h>

#define ll long long
#define INF 2147483647

int inp(){
char c = getchar();
while(c < '0' || c > '9')
c = getchar();
int sum = 0;
while(c >= '0' && c <= '9'){
sum = sum * 10 + c - '0';
c = getchar();
}
return sum;
}

std::pair<int, int> ans[4];
int n;

int query(int x1, int y1, int x2, int y2){
printf("? %d %d %d %d\n", x1, y1, x2, y2);
fflush(stdout);
return inp();
}

int solve1(int cur){
int l = 1;
int r = n;
while(l < r){
int mid = (l + r) >> 1;
if(query(cur, l, cur, mid) & 1)
r = mid;
else
l = mid + 1;
}
return l;
}

int solve2(int cur){
int l = 1;
int r = n;
while(l < r){
int mid = (l + r) >> 1;
if(query(l, cur, mid, cur) & 1)
r = mid;
else
l = mid + 1;
}
return l;
}

int main(){
n = inp();
int cnt = 0;
for(int i = 1; i <= n; i++){
int num = query(i, 1, i, n);
if(num & 1)
ans[++cnt] = std::make_pair(i, solve1(i));
}
if(cnt == 0){
for(int i = 1; i <= n; i++){
int num = query(1, i, n, i);
if(num & 1){
if(cnt == 0)
ans[++cnt] = std::make_pair(solve2(i), i);
else
ans[++cnt] = std::make_pair(ans[1].first, i);
}
}
}
printf("! %d %d %d %d\n", ans[1].first, ans[1].second, ans[2].first, ans[2].second);
fflush(stdout);
}

 评论