题解 CF1762D【GCD Queries】

problem

交互题,评测机有一个排列 \(p:[int]\),值域是 \([0,n)\),现在可以询问 \(2n\)\((x,y)\),评测机回答 \(\gcd(p_x,p_y)\),你需要回答 \(p\)\(0\) 的两个可能的位置。

\(\gcd(x,0)=x\)\(1\leq n\leq 10^4\)

solution with \(4n\) queries

考虑固定死一个数,就 \(p_1\) 吧,询问它和其它所有数的 \(\gcd\)。将这些 \(\gcd\)\(\operatorname{lcm}\) 拎出来,就是 \(p_1\)

然后将所有 \(\gcd(p_1,p_i)=p_1\) 的数拎出来(包括 \(0\)),全部除以 \(p_1\),开始递归。

复杂度分析:如果 \(p_1>1\) 那我们很开心得到一个询问 \(2n\) 次的算法(每次至少折半),但是如果 \(p_1=1\)…………

\[\huge{\text{寄}} \]

而且这样子,我们甚至可以唯一确定 \(0\) 的位置,不太好。

solution with \(2n\) queries

随机三个数 \(i,j,k\),询问 \(x=\gcd(p_i,p_j),y=\gcd(p_i,p_k)\),如果

  • \(x=y\),那么 \(p_i\neq 0\)
  • \(x>y\),那么 \(p_k\neq 0\)(考虑当 \(p_k=0\) 时,\(\gcd(p_i,p_j)>p_i\),这也太离谱了,一个数的约数不能大于他)。
  • \(x<y\),那么 \(p_j\neq 0\)

每次用两次询问排除一个数,询问次数自然是 \(2n\)

code

https://codeforces.com/contest/1762/submission/204571436

// LUOGU_RID: 109661417
#include <queue>
#include <numeric>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
int n;
int query(int x,int y){
	return printf("? %d %d\n",x+1,y+1),fflush(stdout),scanf("%d",&x),x;
}
void report(int x,int y){
	printf("! %d %d\n",x+1,y+1),fflush(stdout),scanf("%*d");
}
int mian(){
	vector<int> a(n);
	iota(a.begin(),a.end(),0);
	while(a.size()>=3){
		int x=a.back(); a.pop_back();
		int y=a.back(); a.pop_back();
		int z=a.back(); a.pop_back();
		int q1=query(x,y),q2=query(x,z);
		if(q1==q2) a.push_back(y),a.push_back(z);
		else if(q1>q2) a.push_back(x),a.push_back(y);
		else a.push_back(x),a.push_back(z);
	}
	report(a[0],a[1]);
	return 0;
}
int main(){
	for(scanf("%*d");~scanf("%d",&n);mian());
	return 0;
}
posted @ 2023-05-08 15:40  caijianhong  阅读(40)  评论(0编辑  收藏  举报