CF1665D GCD Guess 题解

本文迁移自洛谷原文

前置知识:中国剩余定理

我们钦定 ,然后这个 根据辗转相减法就可以变为

所以说,当 的倍数的时候,要求 的倍数。

我们考虑如何充分利用这个性质。

构造一个数 ,和一个数组 ,发现 中是由好多素数构成的。

我们枚举 ,求 ,记为 。如果 是某个 的倍数,那么就可以得到

为什么呢?

首先, 中的数两两互质,所以在 gcd 中互不影响。其次, 的倍数,那么就意味着 的倍数,所以 ,移项就得到了上面那个式子。

最后发现这就是中国剩余定理的板子,直接套即可。

p.s. 这个 正好处于 之间,所以我们选取这些数的积做为模数。

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define reg register
int rem[29];
int pri[]={2,3,7,11,13,17,19,23,29};
ll n,a[16],m[16],Mi[16],mul=1,X;
void exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){x=1;y=0;return ;}
exgcd(b,a%b,x,y);
int z=x;x=y,y=z-y*(a/b);
}
inline void solve(){
for(int i=1;i<=29;++i){
cout<<"? "<<i<<' '<<1293938646+i<<endl;
fflush(stdout);
int x;cin>>x;fflush(stdin);
for(int j=0;j<9;++j)if(x%pri[j]==0)rem[j]=pri[j]-(i%pri[j]);
}
n=9;mul=1;memset(m,0,sizeof(m));X=0;memset(Mi,0,sizeof(Mi));
for(int t=1;t<=n;++t){
m[t]=pri[t-1];
mul*=m[t];
a[t]=rem[t-1];
}
for(int t=1;t<=n;++t){
Mi[t]=mul/m[t];
ll x=0,y=0;
exgcd(Mi[t],m[t],x,y);
X+=a[t]*Mi[t]*(x<0?x+m[t]:x);
}
cout<<"! "<<X%mul<<'\n';
}
int main(){
// ios_base::sync_with_stdio(false);
// cin.tie(0),cout.tie(0);
int T=1;
cin>>T;
for(;T--;)solve();
return (0-0);
}