hogecoder

tsutaj 競技プログラミングの記録

Codeforces Round #527 Div.3 C: Prefixes and Suffixes

問題概要

原文 → Problem - C - Codeforces

 n 文字の文字列  s があり、あなたは  s が全体としてどのような文字列であるかは知らない。しかし、長さが  1 から  n-1 までの全ての prefix と suffix についての情報は持っている (ただし、どれが prefix でどれが suffix かはわからない)

与えられるそれぞれの文字列について、その文字列が prefix であるか suffix であるかに分類せよ。そのような割当が複数あるならばどれか 1 つを答えればよい。

解説

長さ  n-1 の文字列の情報がふたつあり、これを  X, Y とします。これらの情報から  s 全体が何であるかはある程度推測できます。具体的には、 (X, Y) = (prefix, suffix) の場合と、  (Y, X) = (prefix, suffix) の 2 パターンです。

全体の文字列が推測できたので、あとは条件に合うような割当ができれば良いです。これは check(s, k, assim) のようなメソッドを作ることで実現できます。文字列全体が  s と推測されているときに、次に処理すべきものが長さ  k の prefix 及び suffix であり、その時の割当が assim である、という意味です。 一見 TLE しそうなのですが、条件を満たさないときに探索をしないなど枝刈りをすると高速に動くようです。落ちるケースあったら教えてください。

また、multiset などでどの文字列がいくつあるかを数えながら割当を行う方法もあるようです。Writer による解説はその方針でやっています。

ソースコード

int N;
string par[210], NIL = "Z";

string check(string s, int k, string assim) {
    if(k == 0) return assim;
    
    int idxX = -1, idxY = -1;
    for(int i=0; i<2*N-2; i++) {
        if(par[i].length() != k) continue;
        if(idxX < 0) idxX = i;
        else         idxY = i;
    }

    if(s.substr(0, k) == par[idxX] and s.substr(N-k) == par[idxY]) {
        assim[idxX] = 'P'; assim[idxY] = 'S';
        string res = check(s, k-1, assim);
        if(res != NIL) return res;
        assim[idxX] = assim[idxY] = 'X';
    }
    if(s.substr(0, k) == par[idxY] and s.substr(N-k) == par[idxX]) {
        assim[idxY] = 'P'; assim[idxX] = 'S';
        string res = check(s, k-1, assim);
        if(res != NIL) return res;
        assim[idxX] = assim[idxY] = 'X';
    }
    return NIL;
}

signed main() {
    cin >> N;

    string X = "", Y = "";
    int idxX = -1, idxY = -1;
    for(int i=0; i<2*N-2; i++) {
        cin >> par[i];
        if(par[i].length() == N-1) {
            if(X == "") X = par[i], idxX = i;
            else        Y = par[i], idxY = i;
        }
    }
    
    if(Y.substr(1, N-2) == X.substr(0, N-2)) {
        string cand = Y[0] + X;
        string assim(2*N-2, 'X');
        assim[idxY] = 'P', assim[idxX] = 'S';

        string res = check(cand, N-2, assim);
        if(res != NIL) {
            cout << res << endl;
            return 0;
        }
    }
    if(X.substr(1, N-2) == Y.substr(0, N-2)) {
        string cand = X[0] + Y;
        string assim(2*N-2, 'X');
        assim[idxX] = 'P', assim[idxY] = 'S';

        string res = check(cand, N-2, assim);
        if(res != NIL) {
            cout << res << endl;
            return 0;
        }
    }
    return 0;
}

ACM-ICPC 2018 Asia Yokohama Regional Contest 参加記

コンテストからだいぶ日が開いてしまいましたが、参加記を書きます。

ACM-ICPC 2018 Asia Yokohama Regional Contest に参加しました。昨年と同様に four-t として参加しました。また、同じ大学からチーム Megido も参加しました。

0 日目 (前日の移動)

本当はこの日のうちに東京に到着して明日からの日程に備える、はずでした・・・

あれ???

雪がひどくて、乗るはずだった飛行機が飛ばない。一緒に空港に来ていたえび君の飛行機も飛ぶかどうかあやしい。これ大丈夫なのか・・・?

結局自分は、この日は函館まで移動して、翌朝新幹線で移動することにしました。ちなみにえび 君と TAB 君は深夜に着いたそうです。お疲れ様です・・・

函館に行ったら シンヤカトー さんとエンカしました。適当にネカフェを見つけて就寝。

1 日目 (Registration とか Practice とか)

起床成功。前日に予約した新幹線に乗り込みます。席が全然空いていなくてグランクラスしか取れなかったので、乗ってみました。財布さん・・・

想像していたよりもはるかに豪華でした。ハイクラスの旅をする方や、移動手段に困っている方はぜひご検討ください。

そんなこんなで、なんとか Registration に間に合いました。

Practice は去年とだいたい同じように進みました。えび君がサンプルケースを全部チェックしてくれるようなスクリプトを用意してくれていたので、とても助かりました。

その後チーム紹介をしました。翌日はここに書いてある 4 つの T を大事にして頑張りたいという気持ちになりました。

宿に戻りしばらくすると、前日欠航になったため翌日の便に振り替えていたコーチの tsukasa_diary さんが到着!これで four-t 全員集合!!!

記念 (?) に中華を食べました。えび君がえびを共食いしていた

ABC があったんですが解いた後眠すぎてすぐ寝ました。

2 日目 (コンテスト本番)

朝の集合が早いけど起床成功。コンテスト前の時間はマスコットであそんで過ごした。

そしてコンテスト開始。えび君が A を、自分が B を、TAB 君が C を見ることに。A は実装するも WA が出るようでつらそう。B は map でやれば  O(N^{2} \log N) だし行けるなと思って書いたけど TLE (は?) C はすぐ通ったらしい。はいプロ

少々詰まりながらも A も AC。B はとりあえず違う人が実装したらいいかもねということで交代して、結局 3 人とも実装するが TLE しか出ない。どうなってるんや・・・。

なんか G が解かれているので読んでみる。しばらくすると左に寄せる場合と右に寄せる場合の min を取れば良いことに気づき。実装してみる。こちらは一発で通った、うれしいね。

その後他の問題もいくつか見ながら B を実装するも TLE しか出ない。どうなってるんや・・・。

K がそこそこ解かれていたので考えてみる。いろいろ考えてみた結果、その時点で取っても破綻しない最大のものを選び続ければ良さそうだが、TLE しそう。二分探索できそうではあるけど B のせいで  O(N^{2} \log N) が全く信用できない (想定解はこの計算量らしい、どうなってるんや・・・)。

しばらくいじってると B が通る。結局計算量的には変わらないがメモ化してサボるところはサボるのと、map を回避すればよかったらしい。うーん・・・

K を最後に通そうとするも時間切れ。このへんは実力不足を感じますね。明らかに動きとして悪かったものの、去年よりは良いかなあという結果になりました。

順位は 60 チーム中 31 位で、中央値だったため 株式会社オプト さんから企業賞をいただきました。ありがとうございました!

懇親会で懇親していたらいつの間にか肉がなくなっていました。悲しい。

その後は北大勢で打ち上げ。青島ビールおいしかった。

3 日目 (企業見学)

株式会社 bitFlyer さんと Indeed Tokyo さんと LINE 株式会社 さんの企業見学がありました。見学の内容を喋ってよいかどうかがわからないのでマスコットの癒し画像をもって記事を締めたいと思います。

↑これは bitFlyer で昼食を食べた時の写真で、

↑これは企業見学後に競プロ er でサイゼリヤに行った時の写真です。マスコットはいいぞ。

余談

ICPC の翌日に海外出張があったのですが、帰りの飛行機で遅延が発生し乗り継ぎができずに足止めされました。飛行機運がない・・・

総括

去年よりはまともに戦えたので、少し安心しました。とはいえ理想的な戦い方には程遠いので、もっとチーム練習を重ねて上を目指したいなあと思いました。

弊学で今年 ICPC に出たメンバーの大半は引退せず来年も参加できるため、来年も 2 チーム通過を目指してサークル全体で頑張って行きたいですね。

ARC-E を全部解いたので 10 問選んでみた

この記事は Competitive Programming (1) Advent Calendar 2018 の 17 日目の記事として書かれました。本来なら日本で執筆する予定だったんですが、飛行機運が悪すぎて機内でスマホを使って記事を書いています。限界すぎる。

先日、2018 年 12 月 17 日現在で公開されている ARC-E を全て埋めたので、個人的にオススメの問題を 10 問ピックアップして紹介していきたいと思います。自分の好みが原因でジャンルが偏ってるんですが、まあお許しください・・・。

以下ネタバレを含むので、注意です

絶対に落としたくない問題

以下で紹介する問題は、たしかに難しいけれどもコンテスト中に絶対に落としたくないと感じる問題です。(主観ですが) 他でもよく出てくるような考察を必要とします。

Snuke Line (ARC068, 700 点)

この問題は、条件を少し言い換えるだけでかなり見通しが良くなります。ただし言い換えた後も工夫しないと想定解法の計算量にはたどり着かないでしょう。これは自力で思いつきたい問題です。

N! / K 番目の単語 (ARC047, 旧 ARC-C)

よくある問題設定ですね。ここで使う考察は他の問題でもよく出てくるので、是非一度トライすると良いと思います。

キャンディーと N 人の子供 (ARC059, 800 点)

おそらく 800 点の中では簡単な方だと思います。少し高速化しないと AC は取れませんが、他に比べればそれほど難しくはありません。

Meaningful Mean (ARC075, 600 点)

これもよくある設定の問題です。わかってしまえばかなり素直に解くことができますが、自力でこのアプローチを思いつくのは難しいかもしれません。上位勢を目指すなら絶対に外せない枠になるでしょう。

考察重視の問題

以下で紹介する問題はいわゆるアドホック系で、考察寄りの問題です。筆者はこういう系統が一番苦手なのですが、数をこなして考察力をつけるしかないのでしょうかね・・・?

Papple sort (ARC088, 800 点)

問題設定自体は非常にシンプルです。解法も分かればかなりシンプルなものですが、考察力がないと導くのは難しいと思います。普段から考察がちゃんとできているかで明暗が分かれそうな問題です。

Ball Coloring (ARC073, 700 点)

個人的にかなり苦労した問題です。考慮すべき状態の数がそこそこ多いので、その全てを考えきれるかが大きなポイントです。このようにいくつかの場合に切り分けるような問題は解くのに時間がかかりますが、地道にやっていくしかありませんね・・・。

GraphXY (ARC089, 900 点)

グラフの構築問題です。構築系はアドホックな考察が必要とされることが多いと感じていて、これもその一つです。まずは解説を見ずに、じっくり考えてみることをお勧めします。

高度典型を養う問題

以下で紹介する問題は、「このテクニックは自分が知る限りだとこの問題でしか練習できないし難しいけど、大事だよな〜」と思った、いわゆる高度典型の問題です。ARC-E にはたまにこういう問題も混じっているので面白いです。ただ、解くまでに無限時間かかるんですけどね・・・。

Smuggling Marbles (ARC086, 1000 点)

部分点解法は操作に関する簡単な特徴をつかむことで比較的楽に解くことができます。ただし、満点解法はそうはいきません。詳しくはご自身で解説を読んでいただきたいのですが、なんと部分点解法のアイデアを変えずに高速化することで解くことができます。ここで使うテクニックは自分の知る限りではこの問題でしか見たことがないのですが、上位勢からすると典型とのことなので、類題はいくつかあると思われます。たぶん。

NarrowRectangles (ARC070, 1000 点)

この問題は、自分が ARC-E を埋める際に最後の砦的存在になっていた問題で、解くまでに相当時間がかかりました。

問題設定は非常にシンプルで、慣れていれば部分点解法も容易に思いつきますが、満点解法は関数の性質をうまく利用しなければならず、思いついたり実装したりするのは至難の業です。いつかこのような問題を本番中に解きたいものですね。

MUL (ARC085, 700 点)

言わずと知れた有名高度典型です。この問題をきっかけに、某テクニックを知った方も多いのではないでしょうか?最近でいうと CODE THANKS FESTIVAL G 問題で、これに似た考察で解けるものが出題されました。

というわけで、ARC-E から 10 問ピックアップしてみました。当然この 10 問だけ取り組んでも不十分で、やはり全部解くと良いと思います (害悪)

ここまで読んでくださりありがとうございました。来年の今頃には ARC-F が全部埋まってればいいんですが、できているかなあ。