[CF2209C] Find the Zero 题解

[CF2209C] Find the Zero 题解

一道交互题。

题目传送门

题目大意

告知一个整数 \(n\) ,并生成一个未知的数列 \(a\),其长度为 \(2\times n\),且 1~n 中每个整数只在这个数列中出现一次,其余位置使用 0 填充。

现在你可以询问如下问题:

​ 选择两个整数 \(i\)\(j\) ,judge 会告诉你 \(a_i\)\(a_j\) 是否相等。

请在 \(n+1\) 次询问之内找出至少一个 0 的位置,并输出。

补充

judge 为了尽可能使你出错,可能在你询问之后改变这个未知数列,使其在符合之前所有的询问的情况下尽可能难猜。意思就是不能使用出错概率无穷小的假算法。

分析

当时做到这里时间还挺充裕,挺适合仔细想想。何况这题挺有意思。

发现这题目设计得很有目的性,\(n+1\) 次询问中的前 \(n\) 次可以显然地把整个数列询问(以下称扫描,简称扫)一遍。

另外能想到一个非常明显的结论是,我们只需要考虑 judge 的返回值全为假的情况就行,因为若有真值,则我们已经找到至少两个零的位置了。

发现这样扫一遍若全为零,则将这个数列两两分段后每段必定包含一个零和一个非零数。

问题变为一次询问中找出两个数中的非零数,显然不可能。

想想其他思路。尝试将问题规模缩减到 2 失败了,下一步可以试试退一步,尝试缩减到 4 .

发现这非常可行啊。四个数里面会包含至多两个非零数(这里假设我们留下的四个数没扫,不妨令其刚好为前四个数,且除了这四个数都扫过一遍。不过可能存在同一个数段中有两个非零数的情况导致这四个数中零的个数多于 2)

四个数三次扫,发现可以构造一个环解决问题,我选择两两询问 \(a_2\)\(a_3\)\(a_4\),若有真则真的那两就是零,若无真的话这里面至少有两个非零数,则 $ a_1 $ 就显然为零。

参考代码

咱习惯于把多测和询问单独拆在两函数里,写得清晰一点。

//#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
bool ask(int a,int b){
	printf("? %d %d\n",a,b);
	fflush(stdout);
	int t;
	scanf("%d",&t);
	return t;
}
void work(){
	int n=0;
	scanf("%d",&n);
	for(int i=n;i>=2;i--){
		bool as=ask(i*2-1,i*2);
		if(as==1){
			printf("! %d\n",i*2-1);
			fflush(stdout);
			return;
		}
	}
	bool as1=ask(2,3);
	if(as1){
		printf("! %d\n",2);
		fflush(stdout);
		return;
	}
	bool as2=ask(2,4);
	if(as2){
		printf("! %d\n",2);
		fflush(stdout);
	}else{
		printf("! %d\n",1);
		fflush(stdout);
	}
}
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		work();
	}
}
posted @ 2026-04-06 22:37  Liyanx_ArtI  阅读(1)  评论(0)    收藏  举报