橋梁管理日誌

日誌と言いながら日次での更新はされない模様

PCK 2018 Final (AOJ 0401) Playing With Stones

問題ページ

解法

grundy数を考える。白石、黒石がともに0個のときこれ以上の遷移ができず、必敗であることが分かる。また、石は等しい個数同士を交換するか取り除くしかないため、最終的に必ずこの状態になる。よって、再帰的にgrundy数を求めることができる。これをメモ化すれば制約が小さいので十分高速である。

実装

提出コード - https://onlinejudge.u-aizu.ac.jp/status/users/mhrb_minase/submissions/1/0401/judge/3748519/C++14

#include<iostream>
#include<algorithm>
#include<set>
using namespace std;

int grundy[201][101];

int dfs(int w, int b){
    if(grundy[w][b] >= 0){
        return grundy[w][b];
    }

    set<int> st;
    if(w > 0){
        st.insert(dfs(w - 1, b));
    }
    if(b > 0){
        st.insert(dfs(w + 1, b - 1));
    }
    for(int i = 1 ; i <= b && i <= w ; ++i){
        st.insert(dfs(w, b - i));
    }

    int res = 0;
    while(st.count(res)){
        ++res;
    }

    return grundy[w][b] = res;
}

int main(){
    cin.tie(0);
    ios::sync_with_stdio(false);

    fill(grundy[0], grundy[201], -1);
    grundy[0][0] = 0;

    int n;
    cin >> n;

    int v = 0;
    for(int i = 0 ; i < n ; ++i){
        int w, b;
        cin >> w >> b;
        v ^= dfs(w, b);
    }

    if(v == 0){
        cout << 1 << endl;
    }else{
        cout << 0 << endl;
    }

    return 0;
}

PCK 2018 Final (AOJ 0400) Maze and Items

問題ページ

解法

0-9, A-J, a-j, S, Tの高々32頂点間の距離を幅優先探索で求める。このとき、A-J及びa-jは通り抜けてはならないことにする。0-9に0-9、A-Jに10-19、a-jに20-29、Sに30、Tに31の番号を割り当て、グラフを作る。数字を持っているかいないかの各状態ごとにダイクストラ法で全頂点対の最短距離を求める。その後、どの順番で数字を回収するかを全探索する。

実装

提出コード - https://onlinejudge.u-aizu.ac.jp/status/users/mhrb_minase/submissions/1/0400/judge/3743802/C++14

#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;

using lint = long long;
using P = pair<int, int>;
using LLP = pair<long long, long long>;

const long long LLINF = 1LL << 61;
const int dx4[] = {1, 0, -1, 0}, dy4[] = {0, 1, 0, -1};

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

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

struct node{
    int id;
    lint cost;

    bool operator<(const node& rhs) const {
        return cost > rhs.cost;
    }
};

int w, h;
string row[1000];
lint s[10][10];

int px[32], py[32];
lint dis[32][32];
lint cost[1 << 10][32][32];

bool check(int y, int x){
    return 0 <= y && y < h && 0 <= x && x < w && row[y][x] != '#';
}

int main(){
    cin.tie(0);
    ios::sync_with_stdio(false);

    cin >> w >> h;

    fill(px, px + 32, -1);
    fill(py, py + 32, -1);
    fill(dis[0], dis[32], LLINF);
    for(int i = 0 ; i < (1 << 10) ; ++i){
        fill(cost[i][0], cost[i][32], LLINF);
    }

    for(int i = 0 ; i < h ; ++i){
        cin >> row[i];
        for(int j = 0 ; j < w ; ++j){
            if('0' <= row[i][j] && row[i][j] <= '9'){
                px[row[i][j] - '0'] = j;
                py[row[i][j] - '0'] = i;
            }else if('A' <= row[i][j] && row[i][j] <= 'J'){
                px[row[i][j] - 'A' + 10] = j;
                py[row[i][j] - 'A' + 10] = i;
            }else if('a' <= row[i][j] && row[i][j] <= 'j'){
                px[row[i][j] - 'a' + 20] = j;
                py[row[i][j] - 'a' + 20] = i;
            }else if(row[i][j] == 'S'){
                px[30] = j;
                py[30] = i;
            }else if(row[i][j] == 'T'){
                px[31] = j;
                py[31] = i;
            }
        }
    }

    for(int i = 0 ; i < 10 ; ++i){
        for(int j = 0 ; j < 10 ; ++j){
            cin >> s[i][j];
        }
    }

    for(int i = 0 ; i < 32 ; ++i){
        if(px[i] < 0){
            continue;
        }
        lint tmpDis[1000][1000];
        fill(tmpDis[0], tmpDis[h], LLINF);
        queue< pair<P, lint> > que;
        que.push({{py[i], px[i]}, 0LL});
        tmpDis[py[i]][px[i]] = 0LL;
        while(!que.empty()){
            auto now = que.front();
            que.pop();
            if(tmpDis[now.first.first][now.first.second] < now.second){
                continue;
            }
            for(int j = 0 ; j < 4 ; ++j){
                int ny = now.first.first + dy4[j], nx = now.first.second + dx4[j];
                if(!check(ny, nx)){
                    continue;
                }else if(chmin(tmpDis[ny][nx], now.second + 1LL) && (row[ny][nx] == '.' || row[ny][nx] == 'S' || row[ny][nx] == 'T' || ('0' <= row[ny][nx] && row[ny][nx] <= '9'))){
                    que.push({{ny, nx}, now.second + 1LL});
                }
            }
        }
        for(int j = 0 ; j < 32 ; ++j){
            if(px[j] < 0){
                continue;
            }
            chmin(dis[i][j], tmpDis[py[j]][px[j]]);
        }
    }

    for(int i = 0 ; i < (1 << 10) ; ++i){
        for(int j = 0 ; j < 32 ; ++j){
            if(px[j] < 0 || (j < 10 && !(i >> j & 1))){
                continue;
            }
            priority_queue<node> que;
            que.push({j, 0LL});
            cost[i][j][j] = 0LL;
            while(!que.empty()){
                node now = que.top();
                que.pop();
                if(cost[i][j][now.id] < now.cost){
                    continue;
                }
                for(int k = 0 ; k < 10 ; ++k){
                    if(dis[now.id][k] >= LLINF || now.id == k){
                        continue;
                    }
                    if(chmin(cost[i][j][k], now.cost + dis[now.id][k])){
                        que.push({k, now.cost + dis[now.id][k]});
                    }
                }
                for(int k = 10 ; k < 20 ; ++k){
                    if(dis[now.id][k] >= LLINF || now.id == k || i >> (k - 10) & 1){
                        continue;
                    }
                    if(chmin(cost[i][j][k], now.cost + dis[now.id][k])){
                        que.push({k, now.cost + dis[now.id][k]});
                    }
                }
                for(int k = 20 ; k < 30 ; ++k){
                    if(dis[now.id][k] >= LLINF || now.id == k || !(i >> (k - 20) & 1)){
                        continue;
                    }
                    if(chmin(cost[i][j][k], now.cost + dis[now.id][k])){
                        que.push({k, now.cost + dis[now.id][k]});
                    }
                }
                for(int k = 30 ; k < 32 ; ++k){
                    if(dis[now.id][k] >= LLINF || now.id == k){
                        continue;
                    }
                    if(chmin(cost[i][j][k], now.cost + dis[now.id][k])){
                        que.push({k, now.cost + dis[now.id][k]});
                    }
                }
            }
        }
    }

    LLP ans = {LLINF, LLINF};
    vector<int> v;
    for(int i = 0 ; i < 10 ; ++i){
        v.emplace_back(i);
    }
    do{
        if(cost[0][30][v[0]] >= LLINF || cost[(1 << 10) - 1][v[9]][31] >= LLINF){
            continue;
        }
        LLP tmp = {cost[0][30][v[0]] + cost[(1 << 10) - 1][v[9]][31], 0LL};
        bool flag = true;
        int state = 1 << v[0];
        for(int i = 0 ; i < 9 ; ++i){
            if(cost[state][v[i]][v[i + 1]] >= LLINF){
                flag = false;
                break;
            }
            tmp.first += cost[state][v[i]][v[i + 1]];
            tmp.second -= s[v[i]][v[i + 1]];
            state |= (1 << v[i + 1]);
        }

        if(flag){
            chmin(ans, tmp);
        }
    }while(next_permutation(v.begin(), v.end()));

    if(ans.first < LLINF){
        cout << ans.first << " " << -ans.second << endl;
    }else{
        cout << -1 << endl;
    }

    return 0;
}

ICPC2019 国内予選 参加記

ICPC2019 国内予選に参加しました。nicklawさん、NOSSさんと私のチームで4完41位、学内1位で予選突破しました。

チームメイトの参加記

やったこと

A問題は縦方向の和のmaxなので私が秒で実装し、通しました。Bをnicklawさんに書いてもらっている間に、私がC、NOSSさんがDを考察していました。

Cを生やした(つもりになっていましたが、誤読でした・・・)のでDの概要をNOSSさんに聞いて、しばらく考察していました。するとNOSSさんが「dpでは?」と言い、何やら遷移を生やし始めたので信じることにしました(実装の方針などを私も少し考えました)。

BがWAったので私がCを書き始めたのですが、この時点で誤読に気づいてめちゃくちゃ焦りました。間もなくBが修正できたようなので実装をしてもらうことにして、Cを解き直していました。nicklawさんがBを通したので、Cの解き直しを2人で行いました。

解法は、各分銅に対して使わない、左に乗せる、右に乗せるを全探索して、(右に乗せる重さの和 - 左に乗せる重さの和)を列挙します。これで全てのaを作れれば0、そうでないなら作れなかったもののうち1つに対して、a - (右に乗せる重さ - 左に乗せる重さ)を追加する分銅の重さの候補として列挙します。これらの候補及び既存の分銅を用いて全てのaを作れるか判定する、といった感じでした。

これを実装すると無限にサンプルが合わないのでプリントデバッグに入り、NOSSさんがDの実装を始めました。後でわかったのですが、合わなかった原因は、追加する分銅を左に乗せるか右に乗せるかを考慮するのを忘れていたことでした。

この間2人でCとDを書いてはバグらせ書いては無限時間実行され書いてはバグらせを繰り返し、その間nicklawさんも含めてバグ探しに専念しました。2完が見えて焦り始めていたところでCとDを立て続けに通せました。Cは先述したところの修正をしたら通りました。Dは計算量的にやばかったのですが、コンパイルオプション-O3をつけたら爆速で実行されて通すことができました。

この時点で残り1時間ほどでしたが、Eは実装が重そうなので飛ばし、Fを3人で考えました(何も生えませんでした)。

反省

  • 早とちりしないで紙で実装をちゃんと詰めること
  • コンパイル時点での最適化は大事
  • ワンマンプレイをしない

感想(という名のポエム)

まずはなんと言っても、目標にしていたアジア大会出場を曲がりなりにも達成できてよかったです。自分自身としても嬉しいですし、M1の先輩の最後のICPCで国内予選を突破できたことが嬉しかったです。また、おそらくチームのうち誰一人欠けても4完はできなかったと思います(チームメイトもすごかったし、自分が抜けても危うかったと自負してる(ほんまか?))し、チームプレイをちゃんとできたのかなというところが最高でした。小中高通じてこれと言って何かをやった経験があまりなかった自分にとって、大学で競技プログラミングに出会い、打ち込めるようになったこと、それを仲間とともにできる環境があったこと、ICPCに出られたこと、全てが夢のように思え、奇跡であると感じています。このめぐり合わせに感謝しつつ、アジア大会に向けて日々過ごしていきたいなと思います。

AGC033 A Darker and Darker

問題ページ - A - Darker and Darker

概要
HW列のマス目が与えられる。各マスは白、または黒のいずれかに塗られている。全てのマスが黒色になるまで、以下の操作を繰り返す。

  • 白いマスであって、黒いマスと辺を共有するマスを黒くする。

この操作が何回行われるか求めよ。

制約
  • 1 \leq H, W \leq 1000
  • 黒いマスが1つ以上存在する

解法
まず、初めにある黒いマスは1個だけであるという場合を考える。1回の操作で黒いマスと辺を共有するマスが全て黒くなるということは、そのマスが黒くなるのに必要な操作回数は、黒マスを始点としたグリッドグラフの最短路の長さと同じである。よって、全体で行われる操作回数は、黒マスから一番遠い頂点までの最短路の長さと同じと言える。これは幅優先探索で求めることができる。では、黒いマスが2個以上ある場合はどうだろうか。この場合も似たような手順で求めることができる。単一始点の最短経路問題では、初めにキューに始点を入れて探索を始める。同様に、黒いマスが2個以上ある場合は予め全ての黒いマスをキューに入れてから探索を始める。キューの先頭から順にコストを並べたとき、単調非減少であればよいため、この方法で答えを求めることができる。

提出コード
https://atcoder.jp/contests/agc033/submissions/5298324

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

const int INF = (1 << 30) - 1;

/* マス(y,x)と辺を共有するマスは、(y,x+1), (y+1,x), (y,x-1), (y-1,x)
   の4つであるため、このような配列を用いて効率的に探索できる */
const int dy4[] = {0, 1, 0, -1}, dx4[] = {1, 0, -1, 0};

int main(){
    // 入力
    int h, w;
    cin >> h >> w;

    vector<string> a(h);
    for(int i = 0 ; i < h ; ++i){
        cin >> a[i];
    }

    // 幅優先探索
    queue< pair< pair<int, int>, int> > que;  // {{y座標, x座標}, コスト}
    vector< vector<int> > cost(h, vector<int>(w, INF));  // 最小コスト

    // 黒いマスをキューに入れる
    for(int i = 0 ; i < h ; ++i){
        for(int j = 0 ; j < w ; ++j){
            if(a[i][j] == '#'){
                que.push({{i, j}, 0});
                cost[i][j] = 0;
            }
        }
    }

    // 最小コストの更新が止まるまで探索
    while(!que.empty()){
        auto now = que.front();
        que.pop();
        if(cost[now.first.first][now.first.second] < now.second){
            continue;
        }
        cost[now.first.first][now.first.second] = now.second;
        for(int i = 0 ; i < 4 ; ++i){
            int ny = now.first.first + dy4[i], nx = now.first.second + dx4[i];
            if(0 <= ny && ny < h && 0 <= nx && nx < w && cost[ny][nx] > now.second + 1){
                cost[ny][nx] = now.second + 1;
                que.push({{ny, nx}, now.second + 1});
            }
        }
    }

    // 最小コストの最大値を求める
    int ans = 0;
    for(int i = 0 ; i < h ; ++i){
        for(int j = 0 ; j < w ; ++j){
            ans = max(ans, cost[i][j]);
        }
    }

    cout << ans << endl;

    return 0;
}

AtCoder青になりました


先日行われたTenka1 Programmer Contest2019でレートが1657になり、青色に到達しました(なお執筆時点のレートは1642な模様)。300-600-800-800のセットの1完早解き黄パフォで青になっており、多少後味は悪かったですが、ともかく色が変わって嬉しかったです。ここでは、n番煎じながらやったこと等をまとめたいと思います。


~灰

大学の競技プログラミング部に入部しました。AtCoder Programming Guide for beginners(APG4b)で、先輩方に教えていただきつつ競プロに使う言語であるC++の基礎を学びました。その後、コンテストに1回参加しました。

学んだアルゴリズム
この頃はif文とfor文が書ける程度で、単純な演算や繰り返し処理ができた程度だと思います。

~茶

競技プログラミング部での活動として、バーチャルコンテストをやっていました。問題セットはABCの過去問でした。また、AtCoder Beginners Selectionを埋めたり、AIZU ONLINE JUDGEのLessonやLibraryをちまちまとやったりもしました。

学んだアルゴリズム
  • 多重for文による全探索
  • 約数列挙
  • 素数判定
  • 配列上の二分探索(下3つを使うようになるのはもう少し後かも)


~緑

狂ったようにABCのA,Bを埋めていました。いわゆる虚無埋めですね。ただ、このおかげでB問題までを解くスピードが上がり、後ろの問題にかけられる時間が増えたと思います。また、実装にどう落とし込むのかというのが分かってきたところで、300点問題を通せるようになってきました。

学んだアルゴリズム


~水

まず、C問題を確実に時間内に解けるように、過去問のC問題を埋めていました。C問題がほぼ確実に解けるようになった頃には、自然と400点のD問題を自力で解けるようになっていました。先述した競技プログラミング部内でのバーチャルコンテストや、部内で競うように精進していたことなどの成果が出たと思います。そして、300点以下を高速で通すマンになりました(流石に青以上の方々と比べられるとアですが)。これにより大失敗を回避できました(実質的な力がついていなくてよくなさそうですね)。このあたりから、競技プログラミング部内のバーチャルコンテストはAOJ Arenaを用いて行われるようになりました。

学んだアルゴリズム
  • bit全探索
  • 二分探索
  • union-find木
  • 素数modの逆元
  • combinationの計算法
  • グラフの最短経路問題(ダイクストラ法、ベルマンフォード法、ワーシャルフロイド法)
  • 最小全域木(コンテスト中にこれを使った記憶はありません)
  • ナップサック問題等の易しめの典型DP
  • メモ化再帰
  • 2次元以上の累積和・いもす法
  • 半分全列挙


~青

300点以下を爆速で通すマンになりました(流石に黄色以上の方々と比べられ(ry。必然的に400点くらいの問題もそこそこのスピード(30分前後?)で通せるようになり、また、ABC+ARC相当の企業コンテストやAGCが続いたこともあり、レート上昇の要因は早解きがかなりのウェイトを占めています(考察力をつけなさ~い)。とは言っても、500,600点程度の問題は3割くらいは通しているはずなので、早解きにも限界があると思われます。400点~600点くらいの得点帯の過去問を解くようにした成果だと思います(にしては突破率が低いような・・・?)。最近では~800点くらいまででコンテスト中に見た問題は復習するようにしています。コンテストに月1回出るか出ないかくらいの頻度でCodeforcesに手を出しています。yukicoderもちょこっとかじりました。

学んだアルゴリズム
  • 座標圧縮
  • セグメント木(これが想定解の問題にはコンテストではほとんど出会っていません)


まとめ

†低得点帯の早解き力†を鍛えましょう(大失敗が少なくなるので)

ABC124参加記

先日行われたAtCoder Beginner Contest 124に参加した。今回は、全てPython3で解いた。

A問題

問題ページ
解法は、愚直にシミュレーションをした。コードは以下の通り。

a, b = map(int, input().split())

ans = 0
for i in range(2):
	if a > b:
		ans = ans + a
		a = a - 1
	else:
		ans = ans + b
		b = b - 1

print(ans)
B問題

問題ページ
これは、西から順に見ていき、今まで見た中での高さの最大値を保持しておくことで各山についてO(1)で判定できる(制約が小さいので2重ループでも間に合うと思われる)。初期値をH_1以下にしておけば、一番西側も同様に処理できる。コードは以下の通り。

n = int(input())
h = list(map(int, input().split()))

ans = 0
maxi = -1

for i in range(n):
	if maxi <= h[i]:
		ans = ans + 1
	maxi = max(maxi, h[i])

print(ans)
C問題

問題ページ
色の種類が2種類で、隣り合った色が異ならなければならない。よって、最終状態は奇数番目が色0で偶数番目が色1、またはその逆しかありえない。そこで、奇数番目、偶数番目それぞれについて色1の個数を数えておくことで、色を変えなければならない個数を求めることができる。コードは以下の通り。

s = input()
s = list(map(int, s))
n = len(s)

cnt = [0] * 2
for i in range(n):
	m = i % 2
	if s[i] == 1:
		cnt[m] = cnt[m] + 1

ans = min((n // 2 + n % 2 - cnt[0]) + cnt[1], cnt[0] + (n // 2 - cnt[1]))
print(ans)
D問題

問題ページ
1回の操作での最適解は、0のみが連続する部分を選ぶパターンである。K回の操作での最適解も、そのような部分を連続したK個選ぶのが最適である。実装方針は、まず、与えられたデータから、左から順に連続する同じ数字の個数の配列を作っておく。例えば、入力が110011110ならば、2, 2, 4, 1という具合にする。この配列の累積和の配列を作り、これをSとすると、S_i - S_{i - 2K or i - 2K - 1}の最大値が答えとなる。i - 2Ki - 2K - 1かは、iにあたるのが0か1かによる。よって、偶奇と先頭で場合分けをすれば良い。コードは以下の通りである。max(i - k * 2, 0)等は、配列外参照の防止や、配列長が2K未満のケースに対応するためである(ちょうどK回ではなくK回以下なのでこの書き方でおk)。

n, k = map(int, input().split())
s = input()
s = list(map(int, s))

v = []
cnt = 1
for i in range(n - 1):
	if s[i] == s[i + 1]:
		cnt = cnt + 1
	else:
		v.append(cnt)
		cnt = 1
v.append(cnt)
m = len(v)

sum = [0] * (m + 1)
for i in range(1, m + 1):
	sum[i] = sum[i - 1] + v[i - 1]

ans = 0
for i in range(m + 1):
	if s[0] == 0:
		if i % 2 == 0:
			ans = max(ans, sum[i] - sum[max(i - k * 2 - 1, 0)])
		else:
			ans = max(ans, sum[i] - sum[max(i - k * 2, 0)])
	else:
		if i % 2 == 0:
			ans = max(ans, sum[i] - sum[max(i - k * 2, 0)])
		else:
			ans = max(ans, sum[i] - sum[max(i - k * 2 - 1, 0)])

print(ans)
感想

pythonの記法に慣れてきた。後から+=と-=があることを知ったので、次回以降はこう書けると思う。

y_n_ucpc合宿参加記

合宿

始まりはいつも突然に
2月6日21:07、横浜国立大学競技プログラミング部(YNUCPC)Slackに、山梨大学競技プログラミング部(YUCPC)から合同合宿の誘いが来てる旨を伝えるメッセージが投下された。その後なんやかんやあり、22:22の時点で既に合宿用のワークスペースが立ち上げられていた。この間僅か1時間15分である。合宿地は箱根、日程は3月23日・24日の1泊2日であった。

参加者
  • 横国 : ToMさん、ニクローさん、プニプニさん、NOSSさん、まほろば(私)
  • 梨大 : commyさん、Maskさん、xueleiさん
  • 阪大 : ゆきりんさん
阪大のゆきりんさんは、xueleiさんが呼んだら来てくださったそうです。

1日目

現地に13:00集合・・・の、はずであったが、9人中4人しか来ていなかった。結局13:20頃に8人揃ったと思う(1人は元々遅れる予定だった)。バチャがこどふぉった。

バチャ
https://onlinejudge.u-aizu.ac.jp/beta/room.html#YNUYUCPC2019/info - コンテストページ
1日目のバチャはA~Hの8問でチーム戦だった。NOSSさんがセットしてくださった。残りの7人を2,2,3人のチームに分けた。私はニクローさんとMaskさんと組んだ。私のチームの流れは大体↓みたいな感じだったと思う。
  • チームメイトがA,Bを解く
  • Cの考察が終わっていたので書いた
  • その間にニクローさんがDの考察を終えて、Cを通した直後に実装を始めていたのでEを考察した
  • C→D→Eの3問をチーム全体で19分ほどで通した。完璧
  • Fは3人で考えたが、私が考察を生やしたので書く。バグる。申し訳ないm(_ _)m。なんとか通した
  • その時点でチームメイトのGの考察が仕上がっていた
  • 実装がうまくいっていなかったが、概要を聞いたら私のライブラリを貼ると良さそうだったので、貼った。通った
  • 残り3分時点で、暖めていたHのTLE解を祈りながら投げたらWAだった。虚無
結果は7完ペナ差で1位だった(3人いたのでそれはそう?)。H問題は計算量のlogの出処ガチャURであるところの調和級数で、難しかった(隣のチームが通していた)。

夕食情報

この時点でもう一人が合流した。
部屋で畳にパソコンを置いてAGCオンサイトをやった。暖まった。外ではしゃいでいた別の団体がほどよい雑音を作り出していたことに感謝。


風呂は旅館の浴場(温泉!)を使った。

2日目

役場の放送に叩き起こされた。夢の中で考察が生えかけていただけに残念である。

バチャ
https://not-522.appspot.com/contest/5115126982115328 - コンテストページ
2日目のバチャも8問でチーム戦だった。commyさんがセットしてくださった。3,3,2人のチームに分けた。私はプニプニさんと組んだ。流れは
  • プニプニさんがAを通す
  • その間私はCを考察していて、Aが通った直後に書き始めて、通した
  • その間にプニプニさんがBの考察を済ませ、すぐに実装に入り、通した。私のPCを使っていたが、私が提出する問題を間違えるなどして5分のいらんペナを喰らった。謝罪
  • 私はその後すぐにDを書き、通した
  • E,Fが生えそうで詰めきれない
  • Gは明らかに†破滅†
  • Hは通せそうだったので書いた。通した
  • Eの考察を詰め、プニプニさんに実装をしていただいた
  • 実装の間、私がFの考察を詰めた。Eが通った直後に書き始めた。通した
  • Gの部分点を取りに行った
ここでタイムアップで、7完+部分点で1位だった。2日とも1位を取れて嬉しかった。AtCoder Problemsの黄色い問題が増えた。

その後
観光タイムに突入。この日はABCがあったので、小田原に行く組と大涌谷に行く組に別れて行動した。私はゆきりんさん、ToMさん、ニクローさん、xueleiさんとともに大涌谷に行った。3月だというのに雪が積もっており、気温の低さ故の蒸気のせいか、大涌谷の煙がいつもより多く見えた。黒たまごを買ったのは初めてだったかもしれない。

さいごに

とても楽しかったです。調和級数や巨大数のmodなどの競プロ的な知見も得られ、観光パートも充実していました。よい2日間でした。企画・運営してくださった方々、参加者の皆様にこの場を借りて感謝します。本当にありがとうございました。

また、よろしければ↓もどうぞ

ん、タグに春合宿2019が入ってる?てことはまたやるんですか?