题目
问题描述
服务器里面隐藏了一个长度为 n n n的序列,目标是通过不多于 2 ∗ n 2*n 2∗n次访问得出其中第 k k k小的值。
首先由服务器输入序列的长度 n n n和第 k k k小的数据的序号 k k k,
每次访问输出两个坐标:
i
i
i跟
j
j
j,有两种访问方式可以选择,要么得知
a
[
i
]
a[i]
a[i]与
a
[
j
]
a[j]
a[j]位运算相与的结果;要么得知
a
[
i
]
a[i]
a[i]与
a
[
j
]
a[j]
a[j]位运算相或的结果。
然后会把结果输入
最后如果通过访问足以得知结果了,就会把结果输出。
分析
这道题有一点比较恶心,就是那个样例让人摸不着头脑,完全是个陷阱。
要得到第
k
k
k小的数据,还是要先得知每个数之间的大小相关关系,那么结合题目就需要利用两次访问的结果。
有一个结论:
a
+
b
=
(
a
&
&
b
)
+
(
a
∣
∣
b
)
a + b = (a\&\&b) + (a||b)
a+b=(a&&b)+(a∣∣b)
单独讨论某一位就不难分析出这个结论的合理性。
这就意味着我们可以通过两次访问得知两个数的和,那么当我们得知:
{
x
=
a
0
+
a
1
y
=
a
1
+
a
2
z
=
a
2
+
a
3
\begin{cases} x=a_0+a_1 \\ y=a_1+a_2\\ z=a_2+a_3\\ \end{cases}
⎩⎪⎨⎪⎧x=a0+a1y=a1+a2z=a2+a3
在已知
x
,
y
,
z
x,y,z
x,y,z的情况下,也就是解决一个简单的方程就可以得出真正的值。
这样的话我们每次都可以解出三个值,以此作为循环。
若最后有剩余一项或两项,求解就更加方便了,只需要将已知值的一个数与未知数进行两次访问就可以得到二者的和,减法已知数就可以得知那个未知数的值了。
得知了所有数据后,进行排序,输出目标值
A
[
k
]
A[k]
A[k]即可。
这种互动的题目我是第一次见,有一个实时的回馈,比较牛逼。记录一下写法,方便以后参考
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int Array[10001];
signed main(void)
{
register int N,K;
cin>>N>>K;
register int i,X,Y;
for(i=1;i+2<=N;i+=3){
cout<<"and "<<i<<' '<<i+1<<endl<<endl;
cin>>X;
cout<<"or "<<i<<' '<<i+1<<endl<<endl;
cin>>Y;
int A=X+Y;
cout<<"and "<<i<<' '<<i+2<<endl<<endl;
cin>>X;
cout<<"or "<<i<<' '<<i+2<<endl<<endl;
cin>>Y;
int B=X+Y;
cout<<"and "<<i+1<<' '<<i+2<<endl<<endl;
cin>>X;
cout<<"or "<<i+1<<' '<<i+2<<endl<<endl;
cin>>Y;
int C=X+Y;
Array[i]=(A+B+C)/2-C;
Array[i+1]=(A+B+C)/2-B;
Array[i+2]=(A+B+C)/2-A;
}
while(i<=N){
cout<<"and "<<i-1<<' '<<i<<endl<<endl;
cin>>X;
cout<<"or "<<i-1<<' '<<i<<endl<<endl;
cin>>Y;
int A=X+Y;
Array[i]=A-Array[i-1];
i++;
}
sort(Array+1,Array+N+1);
cout<<"finish "<<Array[K]<<endl<<endl;
return 0;
}