22
AtCoder Regular Contest 014 解解 解解解解解 @hamayanhamayan

Atcoder Regular Contest 014 解説

Embed Size (px)

Citation preview

Page 1: Atcoder Regular Contest 014 解説

AtCoder Regular Contest 014解説非公式解説

@hamayanhamayan

Page 2: Atcoder Regular Contest 014 解説

#A君が望むなら世界中全てのたこ焼きを赤と青に染め上げそう

Page 3: Atcoder Regular Contest 014 解説

問題概要• 1 番目のたこ焼きを赤色とする• そこから交互にたこ焼きを青赤と染めたときに N 番目のたこ焼きは何色か• “Red” か” Blue” で出力• 制約 : 1 N 1000≦ ≦

Page 4: Atcoder Regular Contest 014 解説

解法• for 文を使ってシミュレーションする• 奇数番目は赤色、偶数番目は青色になるので N を 2 で割った余りから判断することもできる

Page 5: Atcoder Regular Contest 014 解説

#Bあの日したしりとりの結果を僕達はまだ知らない。

Page 6: Atcoder Regular Contest 014 解説

問題概要• N 行の文字列が与えられており、しりとりを考える• 文字列は「高橋くん」と「高橋クン」の発した単語が時系列順に高橋くんを先攻として交互に与えられている• しりとりのルールは

• 前の単語の、最後のアルファベットから始まる単語を言う• 一度使われた単語をもう一度使わない

• であり、これを先に破ったほうが負けとなる• 高橋くんの勝利なら” WIN” 、敗北なら” LOSE” 、勝敗が決まっていないなら” DRAW” を出力• 1 N 100≦ ≦• 文字列は 20 文字以内の小文字のアルファベットから成る文字列

Page 7: Atcoder Regular Contest 014 解説

解法• これも for 文を使ってシミュレーションする• 「一度使われた単語をもう一度使わない」を実現する為に重複チェックをする必要がある• C++ であれば set を使うとよい

• 要素の追加 O(log n)• 要素の検索 O(log n)

Page 8: Atcoder Regular Contest 014 解説

#C魂の還る場所

Page 9: Atcoder Regular Contest 014 解説

問題概要• 両端が空いている円筒に、ギリギリ入る赤青緑の 3 種類のボールを入れるとする• 入れる場合のルールは

• 両端のどちらから入れても良い• 同じ色のボールと接触すると消える

• ボールを入れる順番が与えられるとき、最後に円筒に残るボールの数が最小となるように計画したとき、円筒に残るボール数の最小値は?• 入力

• N (1 N 50) : ≦ ≦ ボールの個数• S : ボールの順番。 R は赤、 G は緑、 B は青のボールを指す

• 部分点• 1 N 15 ≦ ≦ を満たす入力に正解で 30 点

Page 10: Atcoder Regular Contest 014 解説

部分点解法• 円筒にボールを入れる選択肢は 2 通り• 全てのボールの入れ方をシミュレートすると全部で 2^n 通り• 部分点解法であれば、最高で 2^15 通りなのでこれなら間に合う• 深さ優先探索などを使って全通りを試して最小の個数を出力

Page 11: Atcoder Regular Contest 014 解説

満点解法への考察• もっと“賢い”ボールの入れ方はないだろうか• 貪欲法でやってみる• まず「ボールが消せる場合は消した方がいい」• この方針で分岐数を減らしてみる• → これでもまだ TLE します(自分の実装だと)• もっと減らすにはどうすればいい?• 一手先読みをしてみる

Page 12: Atcoder Regular Contest 014 解説

満点解法への考察• 例)円筒  RGBGB  にボール G を入れる

• パターン 1 : GRGBGB• パターン 2 : RGBGBG

• ボール G の次にボール R が来るということを考慮すると、パターン 2 の方が消せるのでベストな選択と言える• なので、消せるボールが無いときでも、一手先を読むことで分岐を抑えることができる

Page 13: Atcoder Regular Contest 014 解説

満点解法• 円筒にボールを入れる動作をシミュレートする• どちら側からボールを入れるかは以下のルール1. どちらかに入れるボールと同じ色のボールがある

• そちらに入れて消す• ちなみに、どちらとも同じボールとなることは無い

2. そうでない• 一手先に入れるボールと同じ色のボールが無い方に入れる

• これで分岐がなくなるので、余裕で間に合う

Page 14: Atcoder Regular Contest 014 解説

補足と他の人• この貪欲法による解が厳密解なのか近似解なのかは分からない• 一応、 AtCoder で提出したら AC もらえた• 以下、他の AC もらってる人やググッて出てきた人の解答からの知見• http://arc014.contest.atcoder.jp/submissions/517641

• Hoshi524 さんの解答• データ構造をうまいこと使えば先読みしなくても高速に処理できて間に合う• (自分は string を切り貼りしてたので遅かったみたい)

• http://arc014.contest.atcoder.jp/submissions/450041• すぎむさんの解答• 偶奇性というのがあるらしい• http://d.hatena.ne.jp/oupo/20130617/1371446904• このサイトで証明があります

Page 15: Atcoder Regular Contest 014 解説

#Dgrepマスター

Page 16: Atcoder Regular Contest 014 解説

問題概要• all 行の文章があり、文字列検索をかけると、 N 行ヒットした• ヒットした行は L1 行目、 L2 行目、… LN 行目と与えられる• この時、 M回以下の動作(クエリ)を実行する• x と y が与えられ、ヒットした各行に対して上に x 行分から下に y行分の範囲がそれぞれ出力される• この時、出力された行数を出力する• なお、被った範囲はある場合は繰り返しカウントしない• 1 all 10^9≦ ≦    1 N min(all, 10^5)≦ ≦    1 M 10^5≦ ≦• Li < Li+1

Page 17: Atcoder Regular Contest 014 解説

愚直な方法• 単純に考えると、 all 行分の配列を用意してヒットしたそれぞれの行の上 x 行目から y 行目の要素をインクリメントして、最後に数えれば良い• 例) all = 7, N = 2, L = {2, 4}, x = 1, y = 1 のとき(入力例 1 のクエリ 1番)

• 1 以上のものを数えると 5 個なので、 5 を出力• これだと TLE,MLE

0 0 0 0 0 0 0 初期値1 2 3 4 5 6 7 行目

1 1 1 0 0 0 0 L=2

1 1 2 1 1 0 0 L=4

Page 18: Atcoder Regular Contest 014 解説

解法に向けて• そもそも all の最大値が 10^9 なので、全行毎に考える系ではない• 個数の少ないヒットした行から考えてみる• ヒットした行の間は無条件で出力される• ヒットした行の間が x + y よりも小さい時にマージが発生する• よって、 Li 行と Li-1 行の間では、 min(x+y, Li – Li-1 - 1) 行分だけ出力される• L1 行目の前の部分は min(x, L1 -1) 行分• LN 行目の後の部分は min(y, all - LN) 行分• この方法で計算すると 1 クエリの計算に O(N) となり、全クエリ実行で O(MN)=O(10^10) なので間に合わないです• http://arc014.contest.atcoder.jp/submissions/539484

Page 19: Atcoder Regular Contest 014 解説

どこがチューニングできる?• 時間がかかっているところはどこか? → やはり行間のループ• 行間の処理はどういうことをしているか

• 行間が x+y 以上であれば、 x+y を足す• 行間が x+y未満であれば、行間を足す

• ここで使えるのが、行間の大きさだけを考えれば良いため、行間を集めた配列はソートしても計算結果に影響しないということ• 行間を集めた配列をソートしたときに

• x+y未満の要素は、総和• x+y 以上の要素は、その要素数 *(x+y)

• をそれぞれ取って足せば良い

Page 20: Atcoder Regular Contest 014 解説

例• L = {1, 3, 6, 10, 14, 15, 20, 22} とすると、行間の配列は以下の通り

• これをソートする

• x+y=3 とすると以下のように計算すればいい

        総和を取る       要素数 *(x+y)  (累積和を使う!!)

1 2 3 3 0 4 1

0 1 1 2 3 3 4

0 1 1 2 3 3 4

Page 21: Atcoder Regular Contest 014 解説

解法• 事前準備

• 各行間の差をとり、昇順ソートする• 各行間の差の累積和の配列を別に用意する

• 各クエリ• L1 行目の前の部分は min(x, L1 -1) 行分• LN 行目の後の部分は min(y, all - LN) 行分• ヒットした行の部分は N 行分• 行間の部分は x+y を超えない境界線を探す( lower_bound などで)• 境界線より前の部分は累積和の配列を用いて総和を求める• 境界線より後ろの部分は要素数 *(x+y) をする• 以上の数を全て足しあわせて出力

Page 22: Atcoder Regular Contest 014 解説

謝辞• D 問題は自力で解けなくて、以下のサイトを参考に作りました• 解説を上げてるコーダーの方々にはいつもお世話になってます• http://torus711.hatenablog.com/entry/20130616/p1• http://ekaing.hatenablog.com/entry/2013/06/17/235747• http://www37.atwiki.jp/uwicoder/pages/2575.html

• 皆さん賢い!