2021年4月25日日曜日

AHC 002 A - Walking on Tiles

AHC 002 A - Walking on Tiles

とりあえず愚直ビームサーチを書いてみると盤面全体を移動できないうちに行き詰まってしまうことがわかる。

こういうのをどうするかという時に手札があまりなくて困るが、安直な対処として、全体を10x10マスの25個のブロックに区切ってブロックを頂点とするグラフでハミルトンパスをひとつ与えて(偶奇によってはハミルトンパスがなくて24ブロックしか渡れないが)それに沿うような経路を探索するというのはありそう。ベスト方針とは思えないので少しためらっていろいろ考えたが、他に大した案を思いつかないので実装した。

現在のブロックをB1B_1、次のブロックをB2B_2とするとき、B1B_1内では幅6000のビームサーチをする。移動先はB1B_1内のいずれかのマスか、B2B_2のマスでB1B_1に隣接しているもの(高々10個)に限る。後者は最高点の経路だけ保持する。B1B_1のビームサーチが終わったらB2B_2のマスでB1B_1に隣接しているものを始点にB2B_2のビームサーチを始める。これで5.2×1065.2 \times 10^6点程度だった。もう少し考えると、B1B_1B2B_2にまたがるタイルを取る/取らないでB2B_2における探索に影響があるので、それらの違いも考慮して経路を保持する(高々10×21010 \times 2^{10}状態だが、平均的にはそれよりだいぶ小さい。) これで5.3×1065.3 \times 10^6台に上がった。ビームサーチの幅を増やすと点はある程度上がるようだったので、最後の数十分は高速化をして幅8000のビームサーチが間に合うようにしたが、更新できなかった。


  • 結果を見ると、2個程度のテストケースで25個のブロックを渡りきる前に行き詰まって終了しているようだ。そうなる可能性は認識していたけれど、seed=0から99まででは大丈夫だったので軽視していた。応急処置としては、時間があまりすぎていたらもう一回別のハミルトンパスで探索する等は思いつくが……
    • コンテストが終わったあとで調べていたが、そもそもどう動いても隣接ブロックに移れないか、移れる経路が極端に限られるような配置がたまにあるようだ。
  • 最初の考察で、タイル毎に取るほうを決め打つと普通の最長経路問題になるなと思ってしばらく考えていたが、それを利用する方針は思いつかず。
  • 経路を一部破壊してからつなぎなおす方針で上位に入っている人がけっこういるようだ。これも考えたけれど、4時間で詰められると思えなかったので捨ててしまった。

2021年4月24日土曜日

ABC 199 E - Permutation

ABC 199 E - Permutation

dp[S]:=\operatorname{dp}[S] :=集合SSに含まれる整数を数列の先頭S|S|箇所に(制約を満たすように)配置する場合の数、としてDPできる。SSに整数ttを追加してS{t}S \cup \{t\}に遷移する時、制約はXi=S+1X_i=|S|+1であるようなものだけ調べればよい。

また、状態を入れ換えてdp[S]:=\operatorname{dp}[S] :=集合SSに含まれる位置に整数1,2,...,S1, 2, ..., |S|を配置する場合の数、としてもDPできる。この場合はS|S|に位置ttを追加してS{t}S \cup \{t\}に遷移する時、制約はYi=S+1Y_i = |S|+1であるようなものだけ調べればよい。

前者と後者は同じ形の遷移になっていて、一方を書いて入力のXXYYの順番を「間違える」ともう一方になって同じ答えがでる。これらの数えているものは、数列を置換とみなしたときに逆置換で一対一対応している。

第六回 アルゴリズム実技検定

リアルタイム受験した。O以外を通して94点だった。今回は前半の複数の問題にはまってしまって、辛うじてエキスパートという結果だった。

G - 一日一歩

素直に考えると最短経路問題を解けばよくて、通りたい道の直近で通れる日はRMQ上の二分探索で突き止められるが、Gでそんなことする必要がある? と悩んで時間を消費した。結局、最初に考えた通りに実装した。

H - カンカンマート

$T$の値によって2列にわけてどちらも昇順ソートして、$T=0$をできるだけ取るような構成にしてから$T=1$をひとつひとつ増やしていけばよい……のだが、それが見えずに三分探索などをして時間をかなり消費した。

L - 都市計画

道路の端点がタワーか環状交差点上になければならないという条件を見落としていて、解ける問題に見えなくて困っていた。

M - 等しい数

似た問題をCodeChefで解いたことがあったにも関わらず、長時間はまった。CodeChefの問題は平方分割で解いたのだが、実装が大変だった記憶があるし、この問題のほうがハッシュテーブルへのアクセスが多くなりそうだし、制約も厳しいし、ということで平衡二分木で解くことにした……のだが、複数箇所でバグらせてしまった。

区間を平衡二分木で管理するやつを改造して使うことにして、手持ちのものはmapではない(区間に対応する値がない)ので、区間$[l, r)$に入っている値は別に用意した配列の位置$l$に保持することにしたのだが、それが間違いだったかもしれない。区間の分割・消去・挿入などの操作間で、区間に入っている値を正しく保つための処理が複雑になってしまって、手におえなくなるところだった。

N - 活動

順番を固定できればラッキーと思って調べると、$a_i/b_i$の降順でソートしてよいことがわかるのでソートしてDPする。(しかし、最後の活動の取り方を素朴なDPで正しく扱えているのか自信がなかった。)

O - 最短距離クエリ

先に開いて少し考えたがわからなかった。似た設定の問題としてUTPC 2011 - 巡回セールスマン問題があったのを思い出したが、そもそもこれも解けていなかった。

2021年4月11日日曜日

ABC 198 F - Cube

サイコロが手元にあったので、回しながら考えた。まずはバーンサイドの補題を思い出す:

  • Gが集合Xに作用するとする。この時、作用に関する同値類の総数は次の数に等しい:
1|G|gG|Fg|
  • ただし、Fggのfix、つまりFg={xX | g(x)=x}

したがって、正六面体に対する回転操作で回転前と回転後が重なるものを列挙して、各回転に関するfixの大きさを求めればよい。正六面体に対する回転を分類すると以下の通り:

  • id: 恒等変換、つまり何もしない。1通り。
  • α: ある面の中心Aと向かいにある面の中心Bを結ぶ直線を軸に90°回転する。どちら向きに回転するか述べていないが、ベクトルとみなして反時計回りに回転するとすると、ABBAで別の回転になって3×2=6通りある。
  • α2: ある面の中心Aと向かいにある面の中心Bを結ぶ直線を軸に180°回転する。3通り。
  • β: ある辺の中心Aと向かいにある辺の中心Bを結ぶ直線を軸に180°回転する。辺対が6ペアあるので6通り。
  • γ: ある頂点Aと向かいにある頂点Bを結ぶ直線を軸に120°回転する。頂点対が4ペアあって、やはり回転する方向が2通りあるので4×2=8通り。

合わせて24通りあるので、fixの大きさを求めて全部足して24で割ればよいことになる。上の回転によってどの面とどの面が重なるかを調べると、以下の数え上げをすればよいことがわかる:

  • id: x1+x2+x3+x4+x5+x6=Sの正整数解の総数を求めることに等しい。
  • α: x1+x2+4x3=Sの正整数解の総数を求めることに等しい。
  • α2: x1+x2+2x3+2x4=Sの正整数解の総数を求めることに等しい。
  • β: 2x1+2x2+2x3=Sの正整数解の総数を求めることに等しい。
  • γ: 3x1+3x2=Sの正整数解の総数を求めることに等しい。

id,β,γは単純な重複組み合わせに帰着するが、α,α2がやや面倒に見える。

たとえばαに関しては1/(1z)2(1z4)zS6の係数を求めることに等しく、1/(1z)2=(i+1i)zi(負の二項定理)を利用して1+z4+z8+...と掛けたときにどうなるか考えればよい。(自分は計算をバグらせてWolframAlphaに頼った) → x3を決め打つとx1+x2=S4x3の正整数解がS4x31個だから、x=1(S4x1)を非負の範囲で計算すればいいのか。難しく考えてしまった。


見て何をすればよいかはわかったが、WolframAlphaがなければコンテスト中にはACできていなかった。結局は計算力なんだよな。

mod6で多項式になりそうなのでラグランジュ補間、というのを見てなるほどと思った。とはいえ小さいケースをちゃんと求めるのにもある程度の考察は必要か。

https://q.c.titech.ac.jp/docs/progs/polynomial_division.html
そもそも母関数が書けた時点で貼るだけになるらしい。というか、これを知らなくても漸化式に直して行列累乗はできるのでそれは考えるべきだった。