結果

問題 No.3418 【絶望】30個並列ごちゃ混ぜHit&Blowで遊ぼう!
コンテスト
ユーザー butsurizuki
提出日時 2025-12-16 12:20:50
言語 C++23
(gcc 13.3.0 + boost 1.89.0)
結果
AC  
実行時間 1,115 ms / 5,000 ms
コード長 24,165 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 4,649 ms
コンパイル使用メモリ 319,984 KB
実行使用メモリ 26,228 KB
スコア 9,995,841
平均クエリ数 41.59
最終ジャッジ日時 2025-12-25 01:58:44
合計ジャッジ時間 85,514 ms
ジャッジサーバーID
(参考情報)
judge2 / judge5
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 100
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

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

// ------------------------------------------------------------
// 30-parallel mixed Hit&Blow solver (interactive / local sim)
// ------------------------------------------------------------

static constexpr int LEN = 5;
static constexpr int DIG = 10;
static constexpr int N = 30240; // 10P5
static constexpr int K = 21;    // number of (hit,blow) categories

struct Code {
    array<uint8_t, LEN> d;
    uint16_t mask; // 10-bit
    string s;
};

static int HB_ID[6][6];
static array<pair<int,int>, K> ID_HB;

static inline int popcount16(uint16_t x){
    return __builtin_popcount((unsigned)x);
}

static vector<Code> CODES;

static void build_codes(){
    CODES.reserve(N);
    for(int a=0;a<10;a++) for(int b=0;b<10;b++) if(b!=a)
    for(int c=0;c<10;c++) if(c!=a && c!=b)
    for(int d=0;d<10;d++) if(d!=a && d!=b && d!=c)
    for(int e=0;e<10;e++) if(e!=a && e!=b && e!=c && e!=d){
        Code x;
        x.d = {(uint8_t)a,(uint8_t)b,(uint8_t)c,(uint8_t)d,(uint8_t)e};
        x.mask = (1u<<a)|(1u<<b)|(1u<<c)|(1u<<d)|(1u<<e);
        x.s.resize(LEN);
        x.s[0] = char('0'+a);
        x.s[1] = char('0'+b);
        x.s[2] = char('0'+c);
        x.s[3] = char('0'+d);
        x.s[4] = char('0'+e);
        CODES.push_back(std::move(x));
    }
    // sanity
    if((int)CODES.size()!=N){
        cerr << "code size mismatch: " << CODES.size() << endl;
        exit(0);
    }
}

static inline uint8_t hb_cat(int i, int q){
    const auto &A = CODES[i].d;
    const auto &B = CODES[q].d;
    int hit = (A[0]==B[0]) + (A[1]==B[1]) + (A[2]==B[2]) + (A[3]==B[3]) + (A[4]==B[4]);
    int common = popcount16(CODES[i].mask & CODES[q].mask);
    int blow = common - hit;
    return (uint8_t)HB_ID[hit][blow];
}

// Timer
struct Timer {
    chrono::steady_clock::time_point st;
    Timer(){ reset(); }
    void reset(){ st = chrono::steady_clock::now(); }
    double ms() const {
        return chrono::duration<double, std::milli>(chrono::steady_clock::now()-st).count();
    }
};

// Simple Dinic for small graphs
struct Dinic {
    struct Edge{ int to; int rev; int cap; };
    int n;
    vector<vector<Edge>> g;
    vector<int> level, it;
    Dinic(int n=0){ init(n);} 
    void init(int n_){ n=n_; g.assign(n,{}); level.assign(n,0); it.assign(n,0);} 
    void add_edge(int fr,int to,int cap){
        Edge a{to,(int)g[to].size(),cap};
        Edge b{fr,(int)g[fr].size(),0};
        g[fr].push_back(a);
        g[to].push_back(b);
    }
    bool bfs(int s,int t){
        fill(level.begin(), level.end(), -1);
        queue<int> q; level[s]=0; q.push(s);
        while(!q.empty()){
            int v=q.front(); q.pop();
            for(const auto &e:g[v]) if(e.cap>0 && level[e.to]<0){
                level[e.to]=level[v]+1;
                q.push(e.to);
            }
        }
        return level[t]>=0;
    }
    int dfs(int v,int t,int f){
        if(v==t) return f;
        for(int &i=it[v]; i<(int)g[v].size(); i++){
            Edge &e=g[v][i];
            if(e.cap>0 && level[e.to]==level[v]+1){
                int ret=dfs(e.to,t, min(f,e.cap));
                if(ret>0){
                    e.cap-=ret;
                    g[e.to][e.rev].cap+=ret;
                    return ret;
                }
            }
        }
        return 0;
    }
    int max_flow(int s,int t){
        int flow=0;
        while(bfs(s,t)){
            fill(it.begin(), it.end(), 0);
            while(true){
                int f=dfs(s,t,1e9);
                if(!f) break;
                flow += f;
            }
        }
        return flow;
    }
};

// Local judge (for simulation)
#ifdef LOCAL
struct LocalJudge{
    vector<int> hidden; // size 30
    vector<char> already; // N
    int found=0;
    LocalJudge(uint64_t seed){
        already.assign(N,0);
        // sample 30 distinct
        std::mt19937_64 rng(seed);
        vector<int> all(N);
        iota(all.begin(), all.end(), 0);
        for(int i=0;i<30;i++){
            int j = std::uniform_int_distribution<int>(i, N-1)(rng);
            swap(all[i], all[j]);
        }
        hidden.assign(all.begin(), all.begin()+30);
    }
    array<int,K> ask(int qidx){
        array<int,K> hist{};
        for(int s: hidden){
            if(already[s]){
                hist[HB_ID[5][0]]++;
            }else{
                hist[hb_cat(s,qidx)]++;
            }
        }
        // update already
        bool hit = false;
        for(int s: hidden) if(s==qidx){ hit=true; break; }
        if(hit && !already[qidx]){
            already[qidx]=1;
            found++;
        }
        return hist;
    }
};
#endif

// ------------------------------------------------------------
// Solver
// ------------------------------------------------------------

struct Solver {
    // state
    vector<char> alive, asked, forced;
    vector<int> alive_list;

    vector<int> qidxs;                     // asked indices
    vector<array<int,K>> cnt;              // residual histogram for remaining unknown set
    vector<vector<uint8_t>> cats;          // cats[qid][i] category
    vector<uint32_t> zeroMask;             // per query

    deque<int> forcedQ;
    vector<double> w;

    int found=0;

    Timer *timer=nullptr;
    double time_limit_ms = 4950.0;

    Solver(){
        alive.assign(N,1);
        asked.assign(N,0);
        forced.assign(N,0);
        alive_list.resize(N);
        iota(alive_list.begin(), alive_list.end(), 0);
        w.assign(N, 0.0);
    }

    inline bool time_ok() const {
        return timer ? (timer->ms() < time_limit_ms) : true;
    }

    void rebuild_alive(){
        vector<int> nxt;
        nxt.reserve(alive_list.size());
        for(int x: alive_list) if(alive[x]) nxt.push_back(x);
        alive_list.swap(nxt);
    }

    void add_forced(int idx){
        if(!alive[idx] || asked[idx] || forced[idx]) return;
        forced[idx]=1;
        forcedQ.push_back(idx);
    }

    // Heuristic exact enumeration for the final phase.
    // Tries to build (some) full completions of the remaining hidden set
    // consistent with *all* hist constraints, and uses vote frequency
    // to pick a high-confidence next query.
    //
    // This is only used when the remaining count is very small, so the depth is small.
    int refine_by_enumeration(int n_rem, int max_solutions, double budget_ms){
        if(n_rem <= 0) return -1;
        int m = (int)qidxs.size();
        if(m == 0) return -1;
        if((int)alive_list.size() > 3500) return -1;
        if(!timer) return -1;
        // Keep a small safety margin.
        double end_ms = min(time_limit_ms - 40.0, timer->ms() + budget_ms);
        if(end_ms <= timer->ms()) return -1;

        // Copy needs (residual histograms)
        vector<array<int,K>> need = cnt;

        // Choose a pivot query: the one that has the tightest needed bucket.
        int pivot = m-1;
        int best_bucket = INT_MAX;
        for(int qid=0; qid<m; qid++){
            int ac[K] = {0};
            const auto &cat = cats[qid];
            for(int x: alive_list){
                if(alive[x]) ac[cat[x]]++;
            }
            int local_best = INT_MAX;
            for(int r=0;r<K;r++) if(need[qid][r] > 0) local_best = min(local_best, ac[r]);
            if(local_best < best_bucket){
                best_bucket = local_best;
                pivot = qid;
            }
        }

        vector<uint8_t> used(N, 0);
        vector<int> cur;
        cur.reserve(n_rem);
        vector<uint16_t> freq(N, 0);
        int sol_cnt = 0;

        // A small helper: forward-check on pivot only (cheap)
        auto pivot_feasible = [&]()->bool{
            int avail[K] = {0};
            const auto &catp = cats[pivot];
            for(int x: alive_list){
                if(!alive[x] || used[x]) continue;
                avail[catp[x]]++;
            }
            for(int r=0;r<K;r++) if(need[pivot][r] > avail[r]) return false;
            return true;
        };

        function<void(int)> dfs = [&](int depth){
            if(sol_cnt >= max_solutions) return;
            if(timer->ms() > end_ms) return;
            if(depth == n_rem){
                sol_cnt++;
                for(int x: cur) freq[x]++;
                return;
            }
            if(!pivot_feasible()) return;

            // Choose next category in pivot with smallest available pool
            int best_r = -1;
            int best_sz = INT_MAX;
            const auto &catp = cats[pivot];
            for(int r=0;r<K;r++){
                if(need[pivot][r] <= 0) continue;
                int sz = 0;
                for(int x: alive_list){
                    if(!alive[x] || used[x]) continue;
                    if(catp[x] == r) sz++;
                }
                if(sz < need[pivot][r]) return; // impossible
                if(sz < best_sz){
                    best_sz = sz;
                    best_r = r;
                    if(best_sz == need[pivot][r]) break;
                }
            }
            if(best_r < 0) return;

            // Collect candidates in that bucket
            vector<int> cand;
            cand.reserve(best_sz);
            for(int x: alive_list){
                if(!alive[x] || used[x]) continue;
                if(catp[x] == best_r) cand.push_back(x);
            }
            // Prefer high-probability candidates first.
            sort(cand.begin(), cand.end(), [&](int a, int b){ return w[a] > w[b]; });

            for(int x: cand){
                if(sol_cnt >= max_solutions) break;
                if(timer->ms() > end_ms) break;

                used[x] = 1;
                cur.push_back(x);

                bool ok = true;
                // decrement needs for all queries
                for(int qid=0; qid<m; qid++){
                    uint8_t c = cats[qid][x];
                    int &v = need[qid][c];
                    v--;
                    if(v < 0) ok = false;
                }
                if(ok) dfs(depth+1);
                // undo
                for(int qid=0; qid<m; qid++){
                    uint8_t c = cats[qid][x];
                    need[qid][c]++;
                }
                cur.pop_back();
                used[x] = 0;
            }
        };

        dfs(0);
        if(sol_cnt == 0) return -1;

        // Choose the candidate that appears most frequently in enumerated completions.
        int best_idx = -1;
        int best_f = -1;
        for(int x: alive_list){
            if(!alive[x] || asked[x]) continue;
            int f = freq[x];
            if(f > best_f){
                best_f = f;
                best_idx = x;
            }
        }
        // Use only when we have a strong signal and some sample size.
        if(sol_cnt >= 4 && best_idx >= 0 && best_f * 5 >= sol_cnt * 4) return best_idx; // >=80%
        return -1;
    }

    void compute_cats_for_query(int q){
        vector<uint8_t> v(N);
        for(int i=0;i<N;i++) v[i]=hb_cat(i,q);
        cats.push_back(std::move(v));
    }

    void update_zeroMask(int qid){
        uint32_t m=0;
        for(int r=0;r<K;r++) if(cnt[qid][r]==0) m |= (1u<<r);
        zeroMask[qid]=m;
    }

    bool prune_by_zeroMask(int qid){
        uint32_t zm = zeroMask[qid];
        if(!zm) return false;
        bool changed=false;
        vector<int> nxt;
        nxt.reserve(alive_list.size());
        const auto &cat = cats[qid];
        for(int x: alive_list){
            if(!alive[x]) continue;
            if(zm & (1u<<cat[x])){
                alive[x]=0;
                changed=true;
            }else{
                nxt.push_back(x);
            }
        }
        alive_list.swap(nxt);
        return changed;
    }

    // peel discovered code idx from all previous histograms (excluding latest query)
    void peel_found(int idx){
        int m = (int)qidxs.size();
        for(int qid=0; qid<m-1; qid++){
            uint8_t c = cats[qid][idx];
            if(cnt[qid][c]>0){
                cnt[qid][c]--;
                if(cnt[qid][c]==0){
                    zeroMask[qid] |= (1u<<c);
                    prune_by_zeroMask(qid);
                }
            }
        }
    }

    // single-query forced elements: if all remaining in a category must be chosen
    void single_forced(){
        int m = (int)qidxs.size();
        static int ac[K];
        for(int qid=0; qid<m; qid++){
            memset(ac,0,sizeof(ac));
            const auto &cat = cats[qid];
            for(int x: alive_list) ac[cat[x]]++;
            uint32_t eqMask=0;
            for(int r=0;r<K;r++) if(cnt[qid][r]>0 && ac[r]==cnt[qid][r]) eqMask |= (1u<<r);
            if(!eqMask) continue;
            for(int x: alive_list) if(eqMask & (1u<<cat[x])) add_forced(x);
        }
    }

    // Pair pruning using one feasible flow + residual reachability.
    // Returns true if any candidate removed.
    bool pair_prune(int qa, int qb){
        // supplies / demands
        const auto &A = cnt[qa];
        const auto &B = cnt[qb];
        int total=0;
        for(int r=0;r<K;r++) total += A[r];
        if(total==0) return false; // nothing left

        // cap matrix
        static int cap[K][K];
        static int flow[K][K];
        for(int i=0;i<K;i++) for(int j=0;j<K;j++) cap[i][j]=0;
        const auto &catA = cats[qa];
        const auto &catB = cats[qb];
        for(int x: alive_list){
            cap[catA[x]][catB[x]]++;
        }

        // Build max flow network
        // nodes: S=0, rows 1..K, cols 1+K .. 1+2K-1, T=1+2K
        int S=0, A0=1, B0=1+K, T=1+2*K;
        Dinic din(T+1);
        for(int a=0;a<K;a++) if(A[a]) din.add_edge(S, A0+a, A[a]);
        for(int b=0;b<K;b++) if(B[b]) din.add_edge(B0+b, T, B[b]);

        // store edge indices to extract flow
        static int eid[K][K];
        for(int a=0;a<K;a++) for(int b=0;b<K;b++) eid[a][b]=-1;

        for(int a=0;a<K;a++){
            int u=A0+a;
            for(int b=0;b<K;b++) if(cap[a][b]){
                int v=B0+b;
                eid[a][b] = (int)din.g[u].size();
                din.add_edge(u,v,cap[a][b]);
            }
        }

        int fval = din.max_flow(S,T);
        if(fval!=total){
            // should not happen; be conservative
            return false;
        }

        // Extract flow on each A->B edge: reverse edge cap is the used flow
        for(int a=0;a<K;a++) for(int b=0;b<K;b++) flow[a][b]=0;
        for(int a=0;a<K;a++){
            int u=A0+a;
            for(int b=0;b<K;b++) if(eid[a][b]!=-1){
                auto &e = din.g[u][eid[a][b]];
                // reverse edge
                int v = e.to;
                auto &rev = din.g[v][e.rev];
                int used = rev.cap;
                flow[a][b]=used;
            }
        }

        // Residual reachability on bipartite nodes (rows + cols)
        // node: row a -> id a (0..K-1), col b -> id K+b (K..2K-1)
        constexpr int M = 2*K;
        uint64_t reach[M];
        for(int u=0;u<M;u++) reach[u] = (1ULL<<u);

        for(int a=0;a<K;a++){
            for(int b=0;b<K;b++) if(cap[a][b]){
                int fa = flow[a][b];
                if(fa < cap[a][b]){
                    // forward residual row->col
                    reach[a] |= (1ULL << (K+b));
                }
                if(fa > 0){
                    // backward residual col->row
                    reach[K+b] |= (1ULL << a);
                }
            }
        }

        // transitive closure (Floyd with bitsets)
        for(int k=0;k<M;k++){
            uint64_t mk = (1ULL<<k);
            for(int u=0;u<M;u++){
                if(reach[u] & mk) reach[u] |= reach[k];
            }
        }

        // Build impossible / forced_full masks per row a.
        static uint32_t impMask[K];
        static uint32_t fullMask[K];
        for(int a=0;a<K;a++){ impMask[a]=0; fullMask[a]=0; }

        for(int a=0;a<K;a++){
            for(int b=0;b<K;b++) if(cap[a][b]){
                int fa = flow[a][b];
                if(fa==0){
                    bool canInc = (fa < cap[a][b]) && (reach[K+b] & (1ULL<<a));
                    if(!canInc) impMask[a] |= (1u<<b);
                }
                if(fa==cap[a][b]){
                    bool canDec = (fa > 0) && (reach[a] & (1ULL<<(K+b)));
                    if(!canDec) fullMask[a] |= (1u<<b);
                }
            }
        }

        bool changed=false;
        vector<int> nxt;
        nxt.reserve(alive_list.size());
        for(int x: alive_list){
            uint8_t a = catA[x];
            uint8_t b = catB[x];
            if(impMask[a] & (1u<<b)){
                alive[x]=0;
                changed=true;
            }else{
                nxt.push_back(x);
                if(fullMask[a] & (1u<<b)) add_forced(x);
            }
        }
        alive_list.swap(nxt);
        return changed;
    }

    // Run pair pruning against the newest query with many previous queries.
    void pair_closure_latest(){
        int m = (int)qidxs.size();
        if(m<=1) return;
        int last = m-1;

        // When the number of queries is still small, doing a full pair-closure
        // is affordable and often removes a lot of candidates.
        if(m <= 12){
            for(int a=0; a<m; a++){
                for(int b=a+1; b<m; b++){
                    if(!time_ok()) return;
                    pair_prune(a,b);
                }
            }
            return;
        }

        // Process all previous queries vs last (biggest impact)
        for(int i=0;i<last;i++){
            if(!time_ok()) break;
            pair_prune(i,last);
        }

        // Extra closure among a recent window.
        if(!time_ok()) return;
        int W = 6; // recent window
        int st = max(0, last-W);
        for(int a=st; a<last; a++){
            for(int b=a+1; b<last; b++){
                if(!time_ok()) return;
                pair_prune(a,b);
            }
        }
    }

    // IPF weight update (more sweeps when time allows)
    void ipf_update(int sweeps){
        int m = (int)qidxs.size();
        int n_rem = 30 - found;
        if(n_rem<=0) return;
        if(alive_list.empty()) return;
        double base = (double)n_rem / (double)alive_list.size();
        for(int x: alive_list) w[x]=base;

        static double sum[K];
        static double factor[K];

        for(int it=0; it<sweeps && time_ok(); it++){
            for(int qid=0; qid<m; qid++){
                memset(sum, 0, sizeof(sum));
                const auto &cat = cats[qid];
                for(int x: alive_list) sum[cat[x]] += w[x];
                for(int r=0;r<K;r++){
                    if(sum[r]>0) factor[r] = (double)cnt[qid][r] / sum[r];
                    else factor[r] = 1.0;
                }
                for(int x: alive_list) w[x] *= factor[cat[x]];
            }
        }
    }

    int choose_next(){
        // forced first
        while(!forcedQ.empty()){
            int x=forcedQ.front();
            forcedQ.pop_front();
            if(alive[x] && !asked[x]) return x;
        }
        // otherwise IPF
        int sweeps = 110;
        if(timer){
            double rem = time_limit_ms - timer->ms();
            if(rem < 1000) sweeps = 60;
            if(rem < 350) sweeps = 25;
        }
        ipf_update(sweeps);

#ifdef USE_FINISH_ENUM
        // Late-game refinement (experimental): enumerate a few feasible completions and vote.
        {
            int n_rem = 30 - found;
            if(n_rem <= 6 && (int)alive_list.size() <= 4000){
                double rem_ms = timer ? (time_limit_ms - timer->ms()) : 0.0;
                if(rem_ms > 250.0){
                    int cand = refine_by_enumeration(n_rem, /*max_solutions*/28, /*budget_ms*/28.0);
                    if(cand != -1) return cand;
                }
            }
        }
#endif
        int best=-1;
        double bw=-1.0;
        for(int x: alive_list){
            if(!alive[x] || asked[x]) continue;
            double wx = w[x];
            if(wx > bw){ bw=wx; best=x; }
        }
        if(best==-1){
            // fallback
            for(int x=0;x<N;x++) if(alive[x] && !asked[x]) return x;
        }
        return best;
    }

    // Process judge response for query q and histogram hist
    void apply_response(int q, const array<int,K>& hist){
        // invalid query check is handled by judge (all -1,-1) in interactive; in local never.
        int new_found = hist[HB_ID[5][0]];
        bool hit_new = (new_found > found);
        found = new_found;

        // Residual histogram for remaining unknown (excluding found ones)
        array<int,K> resid = hist;
        resid[HB_ID[5][0]] -= found;

        // Add query
        qidxs.push_back(q);
        compute_cats_for_query(q);
        cnt.push_back(resid);
        zeroMask.push_back(0);
        update_zeroMask((int)qidxs.size()-1);

        // mark asked and remove from alive
        asked[q]=1;
        if(alive[q]) alive[q]=0;
        rebuild_alive();

        int qid_latest = (int)qidxs.size()-1;
        prune_by_zeroMask(qid_latest);

        if(hit_new){
            // peel from previous constraints
            peel_found(q);
        }

        // pair pruning & forced detection
        pair_closure_latest();
        single_forced();
    }
};

// ------------------------------------------------------------
// Interactive I/O helpers
// ------------------------------------------------------------

#ifndef LOCAL
static inline bool valid_query(const string& s){
    if((int)s.size()!=LEN) return false;
    bool used[10]={0};
    for(char c: s){
        if(c<'0' || c>'9') return false;
        if(used[c-'0']) return false;
        used[c-'0']=true;
    }
    return true;
}
#endif

int main(){
    // init HB mapping
    memset(HB_ID, -1, sizeof(HB_ID));
    int id=0;
    for(int h=0;h<=5;h++){
        for(int b=0;b<=5-h;b++){
            HB_ID[h][b]=id;
            ID_HB[id]={h,b};
            id++;
        }
    }
    build_codes();

#ifdef LOCAL
    // Local simulation benchmark
    int T = 30; // number of random cases (keep it moderate for local benchmarking)
    uint64_t seed0 = 1;
    long long sumQ=0;
    long long score=0;
    for(int tc=0; tc<T; tc++){
        LocalJudge judge(seed0 + tc*10007ULL);
        Solver solver;
        Timer t;
        solver.timer = &t;
        while(solver.found < 30){
            int q = solver.choose_next();
            auto hist = judge.ask(q);
            solver.apply_response(q,hist);
            if(!solver.time_ok()){
                // stop heavy computations, but keep querying naively
                // (should not happen in local)
                solver.time_limit_ms = 1e9;
            }
            if((int)solver.qidxs.size() > 200){
                cerr << "too many queries" << endl;
                break;
            }
        }
        int Q = (int)solver.qidxs.size();
        sumQ += Q;
        score += 100000 - Q;
        // (no per-case prints)
    }
    cerr << "T="<<T<<" avgQ="<<(double)sumQ/T<<" totalQ="<<sumQ<<" totalScore="<<score<<"\n";
    return 0;
#else
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    Solver solver;
    Timer t;
    solver.timer = &t;

    while(true){
        if(!solver.time_ok()){
            // As a last resort, just keep asking any remaining candidates.
            // (We still want to finish correctly.)
        }
        int q = solver.choose_next();
        cout << CODES[q].s << "\n" << flush;

        // read 30 pairs
        array<int,K> hist{};
        bool invalid=false;
        for(int i=0;i<30;i++){
            int h,b;
            if(!(cin>>h>>b)) return 0;
            if(h==-1 && b==-1) invalid=true;
            if(!invalid){
                int cid = HB_ID[h][b];
                if(cid<0) cid=0;
                hist[cid]++;
            }
        }
        if(invalid){
            // must terminate immediately
            return 0;
        }
        // If smallest pair has hit=5, then all are (5,0) because sorted.
        // We don't have full list, but equivalently hist[50]==30.
        if(hist[HB_ID[5][0]]==30){
            return 0;
        }

        solver.apply_response(q,hist);
    }
#endif
}
0