ラベル 区間DP の投稿を表示しています。 すべての投稿を表示
ラベル 区間DP の投稿を表示しています。 すべての投稿を表示

2023年10月24日火曜日

キーエンスプログラミングコンテスト2023秋(AtCoder Beginner Contest 325)

 Fまで六完で黄色復帰。Fまでは結構速かったのに、一時間以上かけてもGが解けなかったのは反省。

コンテスト後のツイート

G - offence 

 解法ツイートなどを見てAC。

 区間DPと気付いたのは良かったのだが、

・DP[i][j]=区間[i, j)が消せるかどうか

 だと上手くいかない。
 ここで今、DP[i][j]を0/1で持っているが、もっと多くの情報を持たせれば良いのでは? と考えるべきだった。

・DP[i][j]=区間[i, j)が消せるかどうか、そして消せるならさらに何文字消せるか

 を持てば良い。そうすればDPが回る。

 自然な解法で解ける問題だった。

2023年10月7日土曜日

Pinely Round 2 (Div. 1 + Div. 2)

 pretestはEまで。

コンテスト後のツイート

F. Divide, XOR, and Conquer

 こたつがめさんの放送の振り返りを見てAC。
 いや以前に、この放送や公式解説を見たときは分からなくてしばらく放置していたのだけど、今もう一回きちんと見たら理解できた。

 区間DPでいけそう、と言われればそういう気はしてくる。(n=10000でも想定計算量が二乗の可能性があることに注意しよう)
 ただ、区間DPが二乗で済むと思いにくいし、また、空間計算量が線形で済むと気付きにくい。

 区間DPでいけると言われれば自然な解法に思えるが、それでいけるとは思いにくい問題な気がする。

・Sを累積xorとする。
・最上位bitに注目するのは自然。
・長さが長い区間から区間DPしていく。

・[l, r]でS[l]とS[r]の最上位bitが同じなら、[l, n], [n, r] for any n(ただし、それぞれの区間は元のものより短い)に遷移できる。

・[l, r]でS[l]とS[r]の最上位bitが異なるなら、そのbitをxとすると、[l, n]に遷移できるのは、S[l]^S[n]のx bit目が異なるとき([n, r]も同様)。これを覚えておきたいが、そのとき、「S[l]が左端になるときは、右端とx bit目が異なれば良い」とさえ覚えておけば良い。

・この後、xと異なるyについて、「S[l]が左端になるときは、右端とy bit目が異なれば良い」を覚えておきたい状況が存在するかもしれない。そのため、(1<<x)|(1<<y)と記録しておけばOK。



2022年5月23日月曜日

AtCoder Beginner Contest 252

  Fまで六完。

コンテスト後のツイート

G - Pre-Order

 解説を参考にAC。

 コンテスト中考えたことは次のような感じだった。

・「1 2 3 4 5 7 6」という列が与えられ、1から4までは順に一つ前の数が親になったとして、5の親が1になるとすると、(1の最初の子孫である2と3と4を無視して)「1 5 7 6」という配列について考えれば良い。

 これそのままだと(答えは一致するけれど)計算量が上手くいかない。この後、コンテスト中は迷走してしまったが、これを区間DPを用いて高速化しようと考えなくてはいけなかった。

 上では、「1から4までは順に一つ前の数が親になったとして」と、この部分も順に考えていたが、ここは「2 3 4」という配列を考えればOK、と気付くのが重要。

 すると、「1の子として2があるが、その次の子が5」だったとき、求める個数は「2 3 4」と「4 5 7 6」という列の個数の積になる。ここで、「5 7 6」でなく、「4 5 7 6」なのは、この後に出てくる7や6が1に繋がる可能性があるため。一番はじめの数字は何でもいいのだが、区間DPにするため、5の前の数字である4を便宜的に書いている。

 このようにすると、与えられた数列の区間の値により、大きい区間の値を求められると分かったので、区間DPができる。


2021年6月20日日曜日

AtCoder Beginner Contest 206(Sponsored by Panasonic)

  Eまで五完でした。


F - Interval Game 2

 すぬけさんの解説を聞いてAC。
 NIMみたいなことをするのでは? Grundy数を使うのでは? とは多少は考えたものの生かすことができなかった。

 ある一つを選択したら、左右に残った二つの区間のGrundy数のxorがそのGrundy数になる、というのはちょっと思ったのだけど……。

 それに加えて、どういう遷移があるかを考えなくてはいけなかった。
 Grundy数を考えたいのだからどのような遷移があるかを考えるのは当たり前で、「いくつかの区間が選択可能」なときのGrundy数は、それぞれを選択したときのGrundy数のmexとなる。

 Grundy数の定義通りなのだけど、遷移を意識していないと書きにくい気がする。Grundy数と区間DPって相性が良いんだね。

2021年1月16日土曜日

キーエンス プログラミング コンテスト 2021

 Dまで四完でした。一ヶ月くらいレートマイナスが続いていたので、プラスでほっとしました。


C - Robot on Grid

 文字が書かれていないマスでは、2/3の確率で右にいけ(RかXを書き込んだとき)、2/3の確率で下にいける(DとXを書き込んだとき)、と考えると上手くいった。

D - Choosing Up Sides

 このコンテストのEの類題。
 類題と気付くまでも気付いてからも時間がかかってしまったけど、復習したときもよく理解せずACしたので仕方なかったかな、とも。
 「アダマール行列」というキーワードがでてきたので、「高校数学の美しい物語」の該当ページくらいは理解しておきたい。

E - Greedy Ant

 すぬけさんの解説動画を見てAC。
 自分も含め、区間DPっぽいと思った人は多いはず。さらに、制約から三乗が可能そうなので、もう一つ何かパラメーターに持って、DP[l][r][k]のようにできる。ただ、このkを、ターン数のように考えたのでは上手くいかない。

 解説動画では「貯金」という言葉を使っていた。(ツイートを見ると、保留とか予定調和DPという言葉で説明しているものも)
 (問題文中の)すぬけ君はどの飴でも取れるのだけど、区間DPに落とし込みたいなら、今考えている区間の飴を取るようにする他なく、だとすると、他の箇所の飴を取る権利はDPの区間がその場所に来るまで貯めておく……というのは結構自然ですね。

 なんか用語があった方が記憶に残りそうなので、「予定調和DP」という言葉で覚えておくことにしたい。




(解説動画を見たところ、Fはちょっと厳しそうだったので今解くのはやめます。主にFFTへの理解が浅いことが原因です……。勉強しないと。)

2020年2月19日水曜日

Codeforces Round #614 (Div. 1)

 時間がかかってBまでの二完でした。その後、CやEを考えていました。振り返ると、コンテスト中に、Cはほぼ正しい解法が思い付いていたようではあるのですが……。

コンテストへのリンク

A. NEKO's Maze Game

 縦、横、斜めを見て遮っている部分があるかどうか更新していく。その個数を更新する実装が分かりやすかったようです。
 コンテスト中は、setに遮っているindexのpairを放り込む実装をしていました。

B. Aroma's Search

 ($x_i$, $y_i$)達が一直線にあるときを考えると、どこかの点に行って、右か左か一方向へ進むのが最善そう。
 今回は一直線上には存在していませんが、同じように考えても問題ないです。

 公式解説では、その理由として、d(i, j)を($x_i$, $y_i$)と($x_j$, $y_j$)のマンハッタン距離としたとき、d(u, v)+d(v, w)=d(u, w)となることを言っていますね。
 なるほど。ちゃんと考えていませんでした。が、まあ、証明できていなくても、正しいことを考えてはいたので、まあ良いかな、という気持ち。

C. Xenon's Attack on the Gangs

 考察自体はコンテスト中にできていた。(証拠のツイート)

 0、1、……と小さい重みから重みごとに別々に考える発想ができれば、ある葉から葉までのpath上に重みを割り振ることがベストだし、重みwまで割り振った後、次の重みはそれまでに割り振ったedgeと隣接しているedgeに割り振った方が良いことが分かる。
 これは、けんちょんさんのブログの記事に詳しく書いてあります。ここまではコンテスト中に分かっていたし、だから区間DPをすればいいだろう、という方針も立っていた。問題は実装だと思う。

 まず、部分木に含まれる頂点の個数が欲しい。これは、全方位木DPが必要そうに思えるけど、求めた値を全部の頂点個数から引くことで、普通の木DPで求まる。

 その後、けんちょんさんの記事では、点を追加する向き(根に対して遠ざかる方向か、近付く方向か)を求めるのにLCAを使う&再帰を使って実装しているみたい。
 これは非常に関心しました。結構実装の大変な問題だと感じたけど、そういう方針でやればある程度機械的にできそう。

 ただ、PyPyで通そうと思うと、logがつくと厳しそうだし、再帰を使っても多分ダメ。で、まず、向きを求める方は、

・木の辺の向きは親の方向により定まる

 です。そもそも、根を一つ定めたとき、辺の向きは根へ向かう方向か、遠ざかる方向かの二通りしかない。なので、各頂点の親の頂点を記録しておけば向きも分かる。親に対する相対的な向きが欲しいので、思い付いてしまえば当たり前なのだけど、私はなかなか気付けなかった。

 再帰については、普通の直線上の区間DPと同様に、幅1の区間から順番に広げていけば再帰を使わずに書ける。幅1の区間をdequeへ入れておき、BFSの要領でそこから一つずつ広げていく実装が可能。
 さらに、区間両方を広げられるか試す必要はなく、片側は固定しても良い。

 ここまでやってようやく制限時間ギリギリでACできました……。

 いわゆる考察部分はそれほど難しくないと思うけど、実装は結構大変だし、PyPyで通そうと思うと意外と辛い問題でした。