Take a Guess(罪恶陷阱题+互动题+位运算知识: a + b = (a&&b) + (a||b) )

题目

Take a Guess


问题描述

服务器里面隐藏了一个长度为 n n n的序列,目标是通过不多于 2 ∗ n 2*n 2n次访问得出其中第 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)+(ab)

单独讨论某一位就不难分析出这个结论的合理性。
这就意味着我们可以通过两次访问得知两个数的和,那么当我们得知:
{ 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值