0w1

JOI 2011 春合宿 Guess Them All ( Binary Search, Interaction )

http://www.ioi-jp.org/camp/2011/2011-sp-tasks/2011-sp-day2.pdf
guess: 数当て (Guess Them All) - 2011年 日本情報オリンピック春合宿OJ | AtCoder
首先可以用 O( N )把 1的位置給找出來,接著對其他每個數字分別做區間的二分搜,找出它的位置。例如要找 2,可以先詢問左邊的區間 2222..1111,利用已經查出的 1位置推理我們現在想知道在哪個位置的 2是否在左邊。如果單純對每個數字做這樣的區間二分搜,實際的詢問次數最多會到 N + ( N - 1 ) lg( N ),會無法 AC,但是思考一下,什麼時候做了多餘的事,很容易想到例如只剩一個數字待猜,很明顯在哪裡,我們卻在區間亂搜。所以解決方法是,開一個可變長陣列紀錄待猜的位置,我們就只針對這些位置做二分搜,有一點離散的概念。這麼一來每猜一個數字就能減少區間的一個元素,詢問次數也將到 N + sum{ lg( k ) } Ɐ 1 ≤ k ≤ N。
不過這次我真的被這樣的輸入卡到快崩潰還渾然不覺 = =
input:
1
因為在我的 solve()裡,我默認 2必須存在,因此一開始線性猜測 1的位置的時候我都判斷當且僅當有 2個數對應到,才算是找到 1的位置。但事實上這會而且只會被序列長度為 1的時候卡到。到底怎麼會有這麼多 1的測資啊QQ。
f:id:h0rnet:20160301154642p:plain
這個 judge似乎會無視 assert(),以後我要使用它之前一定要記得先判斷這件事。後來用 TLE戰術耗了半個小時才終於找到問題。。我該反省一下,比起用這種雕蟲小技,應該要更清楚這種 corner case才對( 而且真要判的話應該判 corner case是否和錯的重疊,會比較省時 )。
f:id:h0rnet:20160301153516p:plain

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100 + 2;
const int MAXL = 700;

int n, pos[ MAXN ], num[ MAXN ];
vector<int> unknown;

int qry[ MAXN ];

int ask(){
    for(int i = 1; i <= n; ++i)
        printf("%d\n", qry[ i ]);
    fflush( stdout );
    int ret; scanf("%d", &ret);
    return ret;
}

void solve(){
    if( n == 1 ){
        qry[ 1 ] = 1;
        ask();
        return;
    }
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= n; ++j){
            if( j == i ) puts("1");
            else puts("2");
        }
        fflush( stdout );
        int v; scanf("%d", &v);
        if( v == 2 ){
            pos[ 1 ] = i;
            num[ i ] = 1;
            break;
        }
    }
    for(int i = 1; i <= n; ++i){
        if( num[ i ] == 1 ) continue;
        unknown.push_back( i ); // which positions in answer array are remained unknown
    }
    for(int i = 2; i <= n; ++i){
        int lb = 0, rb = unknown.size();
        while( lb + 1 < rb ){
            int mid = ( lb + rb ) / 2;

            memset( qry, 0, sizeof(qry) );
            for(int j = lb; j < mid; ++j)
                qry[ unknown[ j ] ] = i;
            int one_is_counted = 0;
            for(int j = 1; j <= n; ++j)
                if( qry[ j ] == 0 ){
                    qry[ j ] = 1;
                    if( num[ j ] == 1 ) one_is_counted = 1;
                }

            bool is_left;
            if( ask() == 1 + one_is_counted ) is_left = true;
            else is_left = false;

            if( is_left ) rb = mid;
            else lb = mid;
        }
        pos[ i ] = unknown[ lb ];
        num[ unknown[ lb ] ] = i;
        // while( lb < 0 || lb >= unknown.size() );
        // while( unknown[ lb ] < 1 || unknown[ lb ] > n );
        // while( num[ unknown[ lb ] ] == 0 );
        unknown.erase( unknown.begin() + lb );
    }
    // while( unknown.size() == 1 );
    for(int i = 1; i <= n; ++i){
        printf("%d\n", num[ i ]);
        // while( num[ i ] == 0 );
    }
    fflush( stdout );
    int ret; scanf("%d", &ret);
    while( ret != n );
}

int main(){
    scanf("%d", &n);
    solve();
    return 0;
}