Google Code Jam2020 Round1B参加記

今更ですが、GCJ R1Bが4/20 午前1:00(!)にあったので出場しました。流石に時間が遅いので出るか迷いましたが、別にレートがつくわけではないので(ABCもレートがつきませんでしたね)、爆死しても損はないと言うことでダメもとで出場しました。

結果

f:id:andoreiji11:20200420215206p:plain

845位と言うことで、無事通過できました! A問題のテストセット1、2と、B問題をフルマークして合計47点でした。Aはlargeを解きに行こうとして全くの嘘をやっていたので全く通らず、最終的に終了10分前にsmallを通すためのBFSシミュレーションでゴリ押しました。

Bの解説だけ以下記述します。

B問題

概要

2e9四方の正方形盤と、半径がA以上B以下の円形の的が与えられる。あなたは300回まで座標を指定して、そこが的の中心(CENTER)か、的の丸の中(HIT)か、丸の外かの情報を得ることができる。的の中心の座標(CENTER)を得よ。

GCJインタラクティブ問題はテストランナースクリプトpythonで与えられるのですが、自分はこのスクリプトの使い方がわからずqualからずっと放置していました。結局terminalで手でテストして送りました。

解法

この問題は制約が最大の本質で、Hiddenの制約を見ると与えられる半径は5e8以上1e9以下とあるので、(±5e8 or 0,±5e8 or 0)の 9点のいずれか一つは必ずHITになることがわかります。

まず1つのHITを得られた点を(X,Y)としましょう。そうすると、直線x = Xの上で、Xより左側とXより右側に「HITとMISSの境界」がただひとつ存在することがわかるので、その境界を二分探索して求めます。左側の境界をL、右側の境界をRとしましょう。すると、円の中心のx座標Cxは(R + L)/2として求められます。y座標についても同様に求めれば良いです。

//52行目から編集
#include <iostream>
#include <limits.h>
#include <algorithm>
#include <bitset>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <functional>
#include <iomanip>
#include <list>
#include <map>
#include <numeric>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <cassert>

using namespace std;
#define rep(i,n) for(int i=0;i<n;i++)
#define rrep(i,n) for(int i=n-1;i>=0;i--)
#define all(x) (x).begin(),(x).end()
#define mp make_pair
#define pb push_back
#define eb emplace_back

typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<vector<int> > vvi;
typedef vector<vector<ll> > vvl;
typedef pair<int,int> pi;
typedef pair<ll,ll> pl;

template<class T> bool chmin(T& a,T b){if(a > b){a = b;return true;}else return false;}
template<class T> bool chmax(T& a,T b){if(a < b){a = b;return true;}else return false;}
///////////////////////////////////////////////////////////

string s;//judge

long long candr = 1e9 / 2;
long long candx[9] = {0,1,1,0,-1,-1,-1,0,1};
long long candy[9] = {0,0,-1,-1,-1,0,1,1,1};
ll X,Y;

bool output(ll x,ll y){
    cout << x << ' ' << y << '\n';
    cout << flush;

    cin >> s;
    if(s == "CENTER")return true;
    else return false;
}
void main2(){

    // 9箇所のポイントを全部試す
    for(int i = 0;i < 9;i++){
        ll x = candr * candx[i];
        ll y = candr * candy[i];

        if(output(x,y)){
            return;
        }else if(s == "HIT"){
            X = x,Y = y;
            break;
        }
    }
    //(x,y)から上下の境界ポイントを二分探索する
    ll ub = 1e9,lb = Y;
    while(lb + 1 != ub){
        ll mid = (lb + ub)/2;
        if(output(X,mid)){
            return;
        }else{
            if(s == "HIT"){
                lb = mid;
            }else{
                ub = mid;
            }
        }
    }

    ll CYU = lb;
    ub = Y,lb = -1e9;
    while(lb + 1 != ub){
        ll mid = (lb + ub)/2;
        if(output(X,mid)){
            return;
        }else{
            if(s == "HIT"){
                ub = mid;
            }else{
                lb = mid;
            }
        }
    }
    ll CYD = ub;

    ll ansY = (CYU + CYD)/2;


     ub = 1e9,lb = X;
    while(lb + 1 != ub){
        ll mid = (lb + ub)/2;
        if(output(mid,Y)){
            return;
        }else{
            if(s == "HIT"){
                lb = mid;
            }else{
                ub = mid;
            }
        }
    }

    ll CXU = lb;
    ub = X,lb = -1e9;
    while(lb + 1 != ub){
        ll mid = (lb + ub)/2;
        if(output(mid,Y)){
            return;
        }else{
            if(s == "HIT"){
                ub = mid;
            }else{
                lb = mid;
            }
        }
    }
    ll CXD = ub;

    ll ansX = (CXU + CXD)/2;
    output(ansX,ansY);
}


int main(void){
    cin.tie(0);
    ios::sync_with_stdio(false);
    //cout << fixed << setprecision(20);


    int testcase;cin >> testcase;
    ll a,b;cin >> a >> b;
    for(int i = 1;i <= testcase;i++){
        main2();
    }
    return 0;
}
/*
 *!!CHECK!! 
 * 制約をよく読め:まず最も愚直な解法を考えろ
 * intの掛け算をしてる場合:overflowは大丈夫?
 * 制約の下限・上限は大丈夫?
 * 木構造は一直線にした時TLEしない?
 * */

次回Round2は5月?にあると思うので、せっかくのチャンスなので、Tシャツを目指して頑張ろうと思います。