ferinの競プロ帳

競プロについてのメモ

DISCO presents ディスカバリーチャンネル コードコンテスト2020 予選 E - Majority of Balls

問題ページ
 \lbrack i,i+n) \lbrack i+1, i+n+1) にクエリを飛ばしてRBと返ってきた場合,『x 「n-1個』 y」 で2つの区間でRBの数の変動に影響するのはxとyの高々2個しかない.n-1個の部分でRとBの数が異なる場合は不可能.よって  \lbrack i+1, i+n) の範囲内にはRBが (n-1)/2 個ずつ存在する.

このような範囲 + 1個 に対してクエリを投げると,クエリの答えが追加した1個の色と一致する.2n個全てについて,このようなクエリを投げると全てのボールの色を特定できる.

あとは210-99*2=12回で上述したRBが反転するような場所を見つけられればよい.これは  \lbrack lb,ub \rbrack にRとBを両方含むようにしながら,範囲を狭めていく二分探索を行えばよい.\log_2 200 は 8 程度なので問題ない.

わかってたこと

  • RとBを (n-1)/2 個ずつ見つけられれば嬉しい
  • 長さ n区間n+1 個全部試したら2点は確定しそう

わかるべきだったこと

  • RB が連続している部分が必ず存在し,その部分にRBの数が等しい区間が存在する
    多分これがわかれば 3n 回は思いついたはず
  • RBが切り替わる点を一点見つける → 単調性はないが二分探索でできる
  • この制約なら10回あれば \log n くらいの操作はできる
    10回が適当なおまけで 2n 回のクエリにするのだと思ってしまった
#include <bits/stdc++.h>  
using namespace std;  
using ll = long long;  
using PII = pair<ll, ll>;  
#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)  
#define REP(i, n) FOR(i, 0, n)  
#define ALL(x) x.begin(), x.end()  
template<typename T> void chmin(T &a, const T &b) { a = min(a, b); }  
template<typename T> void chmax(T &a, const T &b) { a = max(a, b); }  
struct FastIO {FastIO() { cin.tie(0); ios::sync_with_stdio(0); }}fastiofastio;  
#ifdef DEBUG_   
#include "../program_contest_library/memo/dump.hpp"  
#else  
#define dump(...)  
#endif  
const ll INF = 1LL<<60;  
  
ll col[200] = {0, 1, 0, 1, 1, 0};  
string query(vector<ll> v) {  
#ifdef DEBUG_  
    cout << "?";  
    for(auto i: v) cout << " " << i+1;  
    cout << endl;  
  
    ll cnt0 = 0, cnt1 = 0;  
    for(auto i: v) {  
        if(col[i] == 0) cnt0++;  
        else cnt1++;  
    }  
    string ret;  
    if(cnt0 > cnt1) ret = "Red";  
    else ret = "Blue";  
    cout << ret << endl;  
    return ret;  
#else  
    cout << "?";  
    for(auto i: v) cout << " " << i+1;  
    cout << endl;  
  
    string ret;  
    cin >> ret;  
    return ret;  
#endif  
}  
  
int main(void) {  
    ll n;  
    cin >> n;  
  
    string ls, rs;  
    {   
        vector<ll> v(n);  
        iota(ALL(v), 0);  
        ls = query(v);  
        if(ls == "Red") rs = "Blue";  
        else rs = "Red";  
    }  
  
    ll lb=0, ub=n;  
    while(ub-lb>1) {  
        ll mid = (lb+ub)/2;  
        vector<ll> v;  
        FOR(i, mid, mid+n) v.push_back(i);  
        if(query(v) == ls) lb = mid;  
        else ub = mid;  
    }  
    dump(lb, ub);  
  
    // [ub, ub+n-1) が RB均等  
    vector<ll> r, b;  
    string ans(2*n, '?');  
    REP(i, ub) {  
        vector<ll> v;  
        FOR(j, ub, ub+n-1) v.push_back(j);  
        v.push_back(i);  
  
        string ret = query(v);  
        if(ret == "Red") ans[i] = 'R', r.push_back(i);  
        else ans[i] = 'B', b.push_back(i);  
    }  
    FOR(i, ub+n-1, 2*n) {  
        vector<ll> v;  
        FOR(j, ub, ub+n-1) v.push_back(j);  
        v.push_back(i);  
  
        string ret = query(v);  
        if(ret == "Red") ans[i] = 'R', r.push_back(i);  
        else ans[i] = 'B', b.push_back(i);  
    }  
      
    FOR(i, ub, ub+n-1) {  
        vector<ll> v;  
        REP(j, (n-1)/2) v.push_back(r[j]);  
        REP(j, (n-1)/2) v.push_back(b[j]);  
        v.push_back(i);  
  
        string ret = query(v);  
        if(ret == "Red") ans[i] = 'R';  
        else ans[i] = 'B';  
    }  
  
    cout << "! " << ans << endl;  
  
    return 0;  
}