CF835E-The penguin‘s game【交互】

正题

题目链接:https://www.luogu.com.cn/problem/CF835E


题目大意

长度为 n n n的序列中有两个 y y y其他都是 x x x,给出 n , x , y n,x,y n,x,y。你每次可以询问一个下标集合的数字异或和,要求在 19 19 19次以内找到这两个 y y y的位置。
1 ≤ n ≤ 1000 , 1 ≤ x , y ≤ 1 0 9 , x ≠ y 1\leq n\leq 1000,1\leq x,y\leq 10^9,x\neq y 1n1000,1x,y109,x=y


解题思路

考虑询问一个集合我们会得到的答案情况,如果集合大小为奇数则为 y y y或者 x x x依次表示 y y y分别在一个集合内或者都在某个集合中,而偶数则是 x   x o r   y x\ xor\ y x xor y或者 0 0 0

现在变为了我们可以询问一个集合回答两个 y y y都在一个集合内或外或者一个在内一个在外。

考虑到两个数字的下标肯定有一个二进制位不同,我们可以枚举这个位然后询问这个位是 1 1 1的元素。这样我们总能找到一个集合使得一个 y y y在内,一个 y y y在外。

如果在这个两个集合里面暴力问的话算上前面的次数大概是 3 log ⁡ n 3\log n 3logn的,考虑优化。

发现对于前面的询问,我们可以得到两个集合下标的异或值,所以如果我们问出一个位置再异或出另一个就好了。

因为是 19 19 19次所以我们考虑找比较小的那个集合的值就好了。


code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1100;
int n,x,y,cnt,p[N],ansa,ansb;
void Ask(int l,int r){
	if(l==r){ansa=p[l];return;}
	int mid=(l+r)>>1,ans,flag=0;
	printf("? %d",mid-l+1);
	for(int i=l;i<=mid;i++)
		printf(" %d",p[i]);
	putchar('\n');fflush(stdout);
	scanf("%d",&ans);
	if((mid-l+1)&1)flag=(ans==y);
	else flag=(ans==(x^y));
	if(flag)Ask(l,mid);
	else Ask(mid+1,r);
	return;
}
void Find(int z){
	cnt=0;
	for(int i=1;i<=n;i++)
		if((i/z)&1)p[++cnt]=i;
	if(cnt>(n/2)){
		cnt=0;
		for(int i=1;i<=n;i++)
			if(!((i/z)&1))p[++cnt]=i;
	}
	Ask(1,cnt);
}
int main()
{
	scanf("%d%d%d",&n,&x,&y);
	bool has=0;
	for(int z=1;z<=n;z<<=1){
		int L=0,ans,flag=0;putchar('?');
		for(int i=1;i<=n;i++)if((i/z)&1)L++;
		printf(" %d",L);
		for(int i=1;i<=n;i++)if((i/z)&1)printf(" %d",i); 
		putchar('\n');fflush(stdout);
		scanf("%d",&ans);
		if(L&1)flag=(ans==y);
		else flag=(ans==(x^y));
		if(flag&&!has)Find(z),has=1;
		ansb^=z*flag;
	}
	ansb^=ansa;
	if(ansa>ansb)swap(ansa,ansb);
	printf("! %d %d\n",ansa,ansb);
	fflush(stdout);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值