interactive-gcd-xor#12469

interactive-gcd-xor (Розбір)

Ми будемо перебирати всі прості числа p<5000. Нехай p^k — найбільший степінь , що ділить , а p^l — найбільший степінь , що ділить . Тоді \mathrm{lcm}(a,b)=\Pi_{p<5000 \text{ просте}}p^{\max(k,l)}.

Якщо тепер зробити запит для деякого p^n, такого що p^n\ge\max(p^l,p^k), інтерактор повертає \text{QUERY}(p^n)=\gcd(p^n,a) \oplus \gcd(p^n,b)=\gcd(p^n,p^k) \oplus \gcd(p^n,p^l)=(p^k)\oplus(p^l).

Цього достатньо для розв'язання підзадач і :

У підзадачі відомо, що b=1, тому p^l=1. Отже, можна просто знайти p^k=\text{QUERY}(p^n) \oplus 1.

У підзадачі , оскільки a \neq b, x \neq y, маємо p^k \neq p^l. Без втрати загальності нехай p^k>p^l. Тоді p^k\ge p\cdot p^l\ge2 \cdot p^l, тому старший встановлений біт у p^k не буде змінений XOR-ом з p^l. Водночас, оскільки p\ge 2, старший біт p^k встановлений лише для одного унікального , тому можна знайти з цього (зауважимо, що p^l=\text{QUERY}(p^n) \oplus p^k знаходиться досить легко).

Єдиний випадок, що залишився, — це p^k=p^l, при якому \text{QUERY}(p^n)=0. Оскільки a \neq b, якщо таке трапляється, ми хочемо зберегти на потім. Оскільки a\neq b, існує хоча б одне просте , таке що q^{k1} \neq q^{l1}, без втрати загальності q^{k1} > q^{l1}. Збережемо це q,k1,l1. Пізніше можна перебрати всі раніше нерозв'язані прості і зробити запит \text{QUERY}(q^{k1} \cdot p^n)=(q^{k1} \cdot p^k) \oplus (q^{l1} \cdot p^k). Оскільки q^{k1} \cdot p^n \ge 2 \cdot q^{l1} \cdot p^k, старший біт q^{k1} \cdot p^n залишається встановленим, і оскільки p\ge 2, він однозначно визначає .

#include <bits/stdc++.h>
using namespace std;

#define ll long long

ll PMAX=5000;
ll AMAX=1e9;

ll query(ll c){
    cout<<"? "<<c<<'\n';
    fflush(stdout);
    ll a;
    cin>>a;
    return a;
}

int main() {
    vector<pair<ll,ll>> later;
    ll val=1, c1=-1, c2=-1;

    for (ll i=2;i<=PMAX;i++){
        bool p=1;
        for (ll j=2;j*j<=i;j++) if ((i%j)==0) p=0;
        if (!p) continue;
        ll q=i;
        vector<ll> pot(60,0);
        while (q*i<AMAX){
            pot[__builtin_clz(q)]=q;
            q*=i;
        }
        pot[__builtin_clz(q)]=q;
        ll ret;
        ret=query(q);

        ll r1,r2;
        if (ret==0) later.push_back({q,i});
        else{
            r1=pot[__builtin_clz(ret)];
            r2=ret^r1;
            val*=r1;
            c1=r1;
            c2=r2;
        }
    }
    for (auto p:later){
        ll q=p.first, i=p.second;
        ll snd=c1;
        while (snd*i<AMAX) snd*=i;
        ll ret=query(snd);
        ll ans=1;
        while (((c1*ans)^(c2*ans))!=ret){
            ans*=i;
        }
        val*=ans;

    }
    cout<<"! "<<val<<'\n';
    fflush(stdout);

    return 0;
}