67-ni tapmaq#12267

67-ni tapmaq (Həll təhlili)

Problem Müəllifi:

De3b0o

Hazırlayan:

De3b0o

Redaktə edən:

Hasan Saleh (MathModel)

Həll

fakt: Cavab -dən azdır.

Sübut: Gəlin z = x\mod100 ədədinə baxaq. Əgər z < 67 doğrudursa, onda x + 67-z tuff-dur. Əks halda, əgər z > 67-dirsə, onda x + 167-z tuff-dur ( -yə bərabər ola bilməz, çünki -nin tuff ədəd olmadığı təmin edilir).

x\mod1000 üçün cavabı tapmaq üçün -in son rəqəmini ayrı-ayrılıqda soruşa bilərik və -in cavabı x\mod1000-dən fərqli ola biləcəyi yeganə hal, -in aşağıdakı formada olduğu zamandır:

  • x_1x_2...x_{i-2}x_{i-1}6699...99x_{n-1}x_{n}

Beləliklə, yalnız -in ən kiçikini tapmalıyıq ki, bütün rəqəmlər x_{idx},x_{idx+1},...,x_{n-2} -a bərabər olsun və x_{idx-2} = x_{idx-1} = 6-ya baxmalıyıq, çünki 100 - x\mod100-ni -ə əlavə etsək, bütün rəqəmlər x_{idx},x_{idx+1},...,x_{n} -a bərabər olacaq və x_{idx-2},x_{idx-1} rəqəmləri müvafiq olaraq -yə çevriləcək.

Yuxarıdakıları ikili axtarışla \log(n)+1 sorğu istifadə edərək edə bilərik.


#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define mid ((l+r)/2)
 
using namespace std;

const ll N = 2e5+9;

ll n;

bool check(ll id)
{
    vector<ll> v;
    for(int i = id ; n-2>=i ; i++)
        v.pb(i);
    cout << "? " << v.size();
    for(auto it : v)
        cout << " " << it;
    cout << "\n";
    ll sz;
    bool g = 1;
    cin >> sz;
    if(sz!=1)
        g=0;
    while(sz--)
    {
        ll s;
        cin >> s;
        if(s!=9)
            g=0;
    }
    return g;
}

ll dgit(ll id)
{
    cout << "? " << 1 << " " << id << "\n";
    ll dg;
    cin >> dg >> dg;
    return dg;
}

int main()
{
    ll T;
    cin >> T;
    while(T--)
    {
        cin >> n;
        if(n==-1)
            return 0;
        if(n==1)
        {
            ll x = dgit(n);
            cout << "! " << 67-x << "\n";
            continue;
        }
        ll x = dgit(n-1) , y = dgit(n);
        ll e = x*10 + y;
        if(n==2)
        {
            if(e<67)
                cout << "! " << 67-e << "\n";
            else
                cout << "! " << 167-e << "\n";
            continue;
        }
        ll ans = LLONG_MAX;
        if(e<67)
            ans=min(ans,67-e);
        else
            ans=min(ans,167-e);
        ll z = dgit(n-2);
        if(z==6&&e<70)
            ans=min(ans,70-e);
        ll l = 1 , r = n-2;
        ll id=-1;
        while(l<=r)
        {
            if(check(mid))
                id=mid,r=mid-1;
            else
                l=mid+1;
        }
        if(id!=-1&&id>2)
        {
            cout << "? " << 2 << " " << id-1 << " " << id-2 << "\n";
            ll sz;
            cin >> sz;
            bool g = 1;
            if(sz!=1)
                g=0;
            while(sz--)
            {
                ll s;
                cin >> s;
                if(s!=6)
                    g=0;
            }
            if(g)
                ans=min(ans,100-e);
        }
        cout << "! " << ans << "\n";
    }
}