FHC#R1感想

 

前置き

Facebook Hacker Cup Round1お疲れさまでした。

私はABC3完でしたので、それぞれの感想とかを書いていきます。

 

A問題

(記法:考察→実装の流れ)

証明は面倒なのでしませんが、正しい配管配置は凄く限られます。

1.左端の列と右端の列は、必ず問題文の図の左端,右端にあるような配管しか出来ません。

2.0-indexedとして、2*i+1列目と2*i+2列目(0<=i<N/2)は、必ず問題文の図の1,2列目のように上に凸、もしくは下に凸の配管しかできません。

3.2より、Nが奇数のとき、絶対に配管が不可能なことが分かります。

 

考察は、以上のような流れで行けます。

実装は、まず3番より if (N%2==1)return 0;

1番より、if (左端の列と右端の列のうち必要な場所が埋まってる) return 0;

2番より、求める場合の数を初期値1にしたうえで、

上に凸が出来れば1倍,下に凸が出来れば1倍,両方できるなら2倍になります。

 

雑に言うとこういう流れの実装をすると通ります。

 

B問題

(記法:実装ベースの考察)

AIZUの階段本ありますか?

そこに、木を先行順巡回,後攻順巡回する関数の実装例があります。

これで先に、巡回順番を配列データに起こしましょう。

 

次に、2つの配列データをデバッガなどで見ながら考えてみましょう。

それぞれをA[i],B[i] (0<=i<N)とします。

この時、A[i] == B[i] となるようなラベル割り当てが、問題文での要求です。

そこで、UnionFindを導入しましょう。

A[i]==B[i]なのであれば、併合して同じグループにしてしまうのです。

それを繰り返せば、同じ数字にならなくてはならない頂点同士は、同じグループに居ます。

 

後は、グループがK個以上であれば、それぞれに1~Kのいずれかを割り当てればOKです。(1~Kはそれぞれ1回以上使うことは忘れずに)

 

C問題

(よく分からん)

 

全体の実装順は、前計算O(N*M)→二分探索O(log(Z))→範囲縮小O(N)です。

では順番に見ていきましょう。

 

前計算

・とりあえずHの値は漸化式として与えられています。それを展開しましょう。

パルクールをする方に関しては、担当区間と高さの差の上限下限だけ与えられてます。

そこで、配列limUp(size:N),limDown(size:N)を定義しましょう。

limUp[i] := 地点iから見て、i+1がどれだけ上にあってもパルクール出来るか。

limDown[i] := 地点iから見て、i+1がどれだけ下にあってもパルクール出来るか。

A<B or A>Bで少しだけ混乱しますが気を付けましょう。

 

範囲縮小

前置き

先にこっちの方を考えてみましょう。

先程の前計算結果をどのようにしたら活用できるのかを考えます。

先程の前計算で分かったことは、

・H[i]の値

・H[i+1]の取りうる値の範囲(H[i]基準で)

です。

 

小さな具体例

さて一度、答えを0secと仮定して全体を見渡してみましょう。

H[0]に着目すると、limUp[0],limDown[0]を元に、H[1]の取りうるべき値の範囲が分かりますね。その範囲の中にあれば、全てのパルクール演者はH[0],H[1]間のパルクールが成功します。

これを一般化して、H[i]に着目すれば、limUp[i],limDown[i]を元にH[i+1]への(からの)パルクールが成功するかどうかが判定できます。全てのiで成功すれば、答えが0secでもパルクールが成功します。

 
一般化

さて問題は、0secじゃない場合にはどうなるのか?

これは恐らく、図を書いてみると解きやすいと思います。

まず初めに、答えが t secであると仮定します。

 

区間 0-1における、0の方が取るべき高さの区間

次にH[0]から見たH[0]が最終的に取りうるべき高さを考えます。

(これは換言すると、t secという条件下に置いて、更にH[0]がその時間内に動ける範囲をH[0]自身を用いて定義しているフェーズです。)

これは(H[0] - t) ~ (H[0] + t)-① の区間です。その範囲であれば、t sec以内に高さが調節できます。

 

区間0-1における、1の方が取るべき高さの区間

そしてH[0]から見たH[1]が最終的に取りうるべき高さを考えます。

(これは換言すると、t secという条件下に置いて、更にH[0],H[1] 間の高さの差分の条件を満たすH[1]の高さの範囲を、H[0]を用いて定義しているフェーズです。)

H[0]は先程の結果より、 (H[0] - t) ~ (H[0] + t) の区間で自由に高さを決定できます(ただし下限は0です)。その上でlimUp[0],limDown[0]の情報も付け加えると、結果としてH[1]が最終的に取るべき高さは、(H[0] - t - limDown[0]) ~(H[0] + t + limUp[0])ー② の区間になります。それ以外の高さをH[1]が最終的に取ると、どう頑張ってもパルクール演者の誰かは失敗します。

 

 

さて後は、これを区間1-2以降も繰り返していくだけです。

(ただし次の場合は、①と②の重複部分が、H[1]の取るべき高さの区間になります)(ここ上手く文字にしにくい)

これを、H[i] , H[i+1]それぞれに対して順番にやっていきます。

最終的に可能な区間が残っていれば、、t sec以内にパルクールの準備が可能です。

 

二分探索

さて、所要時間を固定すれば、パルクール成功判定は出来ます。そこで所要時間で二分探索します。

こういう感じでやっていけば、何とか解けます。

感想

C問題の考え方を文字に起こすのめっちゃ難しいし、野生のプロの文章読みながら書き方習います。

ともかく、Round2に進出出来て良かったです。

Round2が午前2~5時にあること、その12時間後くらいに予定があることはヤバいですが、多分大丈夫だと信じて、Tシャツを狙っていきます。

(確かTシャツは上位500人でしたよね?)

 

Round3は、運が良ければ行きたいなぁ・・・

 

以上、Round2進出を確認してから1時間で書き上げてみました。