一个有点乱搞感觉的做法。
直接讲结论:每次取出最小的若干个乘积 的质数相乘询问,每次如果问出 就再问一次判断有几个 的因子,更新当前 和剩下因数乘积的上界 。如果全部询问用完就输出 ,如果枚举到的质数 就输出 。
先讲后一种回答为什么是对的:在这种情况下, 的质数对答案的贡献最多是 ,最少是 ,则输出 一定两倍范围内。
第一种回答就要复杂一点,其满足的是 这种情况。容易发现,我们一定是可以将 内的质数问完的(只需要 次),所以真实答案一定在 的范围内。于是我们就需要保证回答时 。
此时发现若 ,, 的质数需要 次问完,也就是说这种情况会直接用第二种回答!如果 再变小,虽然可能前面需要的询问也会变多,但是毛估估一下 下降的很快,所以是可以保证 时全部都是用第二种回答的。
后来又想到,其实 时似乎也可以只用第二种回答?因为 只用 次,加上前面问 的幂次的 次也是够的,更大就更不用说了。不过 的情况也是要用第一种处理的。
code:
int n,m;
vector<int> g;
bool isPrime(int x){
for(int i=2;i*i<=x;i++){
if(x%i==0){
return 0;
}
}
return 1;
}
il ll ask(int op,ll x){
if(op==1){
printf("! %lld\n",x);
fflush(stdout);
return 0;
}
printf("? %lld\n",x);
fflush(stdout);
ll ret;scanf("%lld",&ret);
return ret;
}
void Yorushika(){
m=1e9;int cnt=0,ans=1;ll sum=1;
const ll mx=1e18;
g.clear();
rep(i,2,1000){
if(isPrime(i)){
if(sum>mx/i){
ll x=ask(0,sum);
cnt++;
if(cnt==22){
ask(1,ans*9/2);
return;
}
for(int j:g){
if(x%j==0){
ll y=j;
while(y<1e9){
y*=j;
}
ll x=ask(0,y),z=0;
m=m/x+1;
while(x>1){
x/=j,z++;
}
ans*=(z+1);
if(++cnt==22){
ask(1,ans*9/2);
return;
}
}
}
if(1ll*i*i*i>m){
ask(1,ans*2);
return;
}
sum=i,g.clear(),g.eb(i);
}else{
sum*=i,g.eb(i);
}
}
}
}
signed main(){
int t=1;
scanf("%d",&t);
while(t--)
Yorushika();
}