Codeforces Round #781 (Div. 2) D. GCD Guess
题目需要求出x,每次操作知道(x+a,x+b);
先假设d=(x+a,x+b),每次我们可以构造出一个同余式x=-a(mod d)
不难发现我们只要构造出足够多的同余式就可以用excrt搞定。
那么怎么选取a就很关键了,或者我们可以试着去构造出一个巧妙的lcm(d1,d2,..dk),并且保证lcm的值大于x的取值范围。
不难发现只需要选取从2到29的质因子就能凑齐大于1e9的lcm,但或许不需要选取那些质因子并且将会更优,虽然也没什么区别就是了。这里是取的d是4,5,7,9,11,13,17,19,23;然后lcm就是1338557220。
同时我们把b成lcm+a。这样保证了,对于x+a的因子,一定也是lcm的因子,这样才能保证excrt的模数范围
我们想要所有的gcd(x+a,x+lcm+a)都是这些d之间的积,那么我们需要保证x+a一定并且至少是一个d的倍数。
我们把目光投向最大的d:23。这个时候我们只需要保证取遍模23的剩余系就好了,另a=1-23,一定有一个数字加上x是23的倍数。
因此就构造出来了一种解法。只需要在写程序之前计算出一组lcm,然后令a取遍最大的d范围内所有数,之后就交给excrt好了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=35,N=1e5+10,inf=2e9;
const long long Lcm=1338557220;
ll get(ll a,ll b)
{
printf("? %lld %lld\n",a,b);fflush(stdout);
ll d;scanf("%lld",&d);
return d;
}
ll a[maxn],m[maxn];
int n=23;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
return x=1,y=0,a;
}
ll d=exgcd(b,a%b,y,x);
y-=a/b*x;return d;
}
ll mul(ll a,ll b,ll mod)
{
ll res=0;
while(b>0)
{
if(b&1) res=(res+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return res;
}
ll excrt()//扩展中国剩余定理
{
ll m1,a1,x,y,bg;
m1=m[1],a1=a[1];
for(int i=2;i<=n;i++)
{
ll m2=m[i],a2=a[i],c=((a2-a1)%m2+m2)%m2;
ll d=exgcd(m1,m2,x,y);
//if(c%d){return -1;}
// if(mul(x,c/d,m2/d)<=0)continue;
x=mul(x,c/d,m2/d);
a1=x*m1+a1;
m1=m2/d*m1;
a1=(a1%m1+m1)%m1;
}
a1=(a1%m1+m1)%m1;
if(a1==0)a1=m1;
return a1;
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
ll x,y;
ll d;
for(int i=1;i<=n;i++)
{
x=i,y=Lcm+i;
d=get(x,y);
m[i]=d;a[i]=d-x%d;
}
printf("! %lld\n",excrt()%Lcm);
fflush(stdout);
}
}