通过这些询问,我们可以知道 n+1,n+2,…n+Y-1分别是哪些正整数的倍数,从而可以推出 n 对这些正整数取模的结果。
例如,我们通过询问得到了 gcd(n+v,n+X+v) 是11的倍数,那么就有 n \equiv -v \left(mod \; 11\right)
利用中国剩余定理解同余方程,即可解出 $n$ 的值。
关于正整数,一种比较优秀的选法是:$\{ 16,9,7,11,13,17,19,23 \}$,可以做到22次询问出结果。
## 代码:
```cpp
#include<bits/stdc++.h>
using namespace std;
const long long NUM=1070845776; //NUM是下面几个数的乘积
const int P[10]={16,9,7,11,13,17,19,23};
int N;
long long M,A[11],m[11],t[11],Ans;
void exgcd(long long a,long long b,long long &x,long long &y){
if(b==0){
x=1;
y=0;
}else{
exgcd(b,a%b,x,y);
long long t=x;
x=y;
y=t-(a/b)*y;
}
}
long long inv(long long a,long long p){
a%=p;
long long x,y;
exgcd(a,p,x,y);
return x>=0?x%p:x%p+p;
}
long long CRT(){ //中国剩余定理解同余方程
N=8,M=NUM;
Ans=0;
for(int i=0;i<N;i++){
m[i]=M/P[i];
t[i]=inv(m[i],P[i]);
Ans=(((m[i]*t[i]%M)*A[i])%M+Ans)%M;
}
return Ans;
}
int T,n,cnt;
long long res;
void solve(){
for(int i=0;i<8;i++){
A[i]=0; //A[i]初始化为0,后面询问没有赋值就说明n自己就是这个数的倍数
}
for(int i=1;i<=22;i++){
printf("? %d %lld\n",i,NUM+i);
fflush(stdout);
scanf("%lld",&res);
for(int j=0;j<8;j++){
if(res%P[j]==0){
A[j]=(P[j]-(i%P[j]))%P[j];
}
}
}
printf("! %lld\n",CRT());
return;
}
int main(){
scanf("%d",&T);
while(T--){
solve();
fflush(stdout);
}
return 0;
}
```