AGC029 C - Lexicographic constraints
解法
種類以下で問題の条件を満たすような文字列たちを作成できるか?という問題を考えると、ある境界が存在し、それ以下では作成不可能、それ以上では作成可能になります。よって、あるについて問題の条件を満たすかどうか、を見ながら二分探索を行えばよいです。
先頭の文字列から順に見ていったときに、次に作成する文字列は、作成できる文字列の中で辞書順最小のものを選んだ場合が一番得をします。
なので、今作成し終えた文字列の状態を見て、次に作成する辞書順最小の文字列が何か?というのがわかればよいです。
文字列をランレングス圧縮のようにして、
文字目までがという文字になっている
という情報で書き換えます。
すると、次に作成する文字列の長さがであったとき、
であれば、辞書順最小の文字を末尾に追加
そうでなければ、すでに作成し終えている文字列の末尾のうち、以下でかつ未満の文字を次の文字に置き換え、その後辞書順最小の文字で埋める
という操作をすればよいです。
上記の条件を満たすような文字が存在しなければ、作成できないということになります。
感想
初手で貪欲に走ったので反省…
Typical DP Contest G - 辞書順
解法
まず、
- 文字目の次にという文字が現れるインデックスの最小値
を計算します。
これは、という更新を行えばよいです。
さて、
- 文字目を先頭で使った際にできる、部分文字列の種類数
というDPを考えます。
文字目を使った際に考えられるパターンは、
文字目の1文字のみ
文字目の次に、という文字を繋げる
の2つです(後者は最大26通りありますが…)。前者は明らかにトータル1通りです。
後者については、の直後に現れるを取ってくると、番目の文字の次にを置いたパターンの部分文字列が網羅できます。
なので、結局
という遷移になります。
では、最終的に求めたい文字列はどうなるでしょうか。
まず、先頭の文字がa,b,...z
であるような部分文字列全てについて足してもにならない場合は、明らかにEel
となります。
それ以外の場合は、前から貪欲、a
から順番に見ていき、総和が以上になるならその文字を選択…という操作を繰り返します。
実装の際は、先頭にダミーの文字を入れると実装が少し楽になります。
今見ているインデックスをとして、次に選ぶ文字をa
から順番に見ていきます。を見て、これが以上ならばが次の文字になります。そうでなければ、からを引き、次の文字をチェックしていきます。
今見ている文字が最後の場合となるパターンをから引き忘れないようにすれば、答えが求まります。
感想
最近ABCやその他の場所で、ある状態を構成し数え上げる際に重複するパターンを、貪欲にチェックする操作のみ考慮することで重複を消す、という考え方を使ってきたので、それと似たような考え方でできました。
それよりむしろ、メモリ制限が厳しかったです…空間計算量はまだまだ難しいですね
AGC009 C - Division into Two
解法
集合を、集合0、集合1とし、をとします。
- 番目の要素まで見て、番目の要素を集合に入れるような振り分け方のパターン数
を数えます。
という区間が集合に入り、その前後はもう片方の集合に振り分けられるパターンを考えます。このような操作ができる条件は、
の二つを満たすことです。
逆に、これらの条件を満たすについて、を集合に入れる、という操作ができるので、
という遷移が考えられます。
1つめの条件は、それぞれのについて二分探索を用いて計算ができます。
となるようなを二分探索で求めます。すると、 として選べるのは以下です。
2つめの条件は前計算ができます。
となるようなで区間を区切っていきます。
番目の要素についてみると、として選べるのは、その要素が所属している区間の要素のみになります。
以上で、ある要素について選べるの区間が求まったので、その区間の総和を求めてあげればDPの遷移が行えます。
区間和による遷移なので、累積和やセグ木で簡単に高速化ができます。
答えはです。
オイラーツアー、HLD解いた問題まとめ
提出場所見失いそうなので個人用まとめです
もう少し解法の中身っぽいところを後で加えたいです(加えてください)
HLD
Submission #12187038 - 2010年 日本情報オリンピック春合宿OJ
Submission #12180953 - AtCoder Beginner Contest 133
#491538 (C++17(1z)) No.529 帰省ラッシュ - yukicoder
HLDのupdate関数を使うこと!!!
オイラーツアー
頂点
辺
Waseda Orientation Programming Contest 2020 J - トレジャーハンター
問題概要
種類のはしごがあり、番目のはしごの長さはです。
以上以下の値で、以下の条件を満たさないものの個数を求めてください。
- 種類のはしごを上手く使い、長さの総和をにすることができる(ただし、同じはしごは何回でも使用可能、1度も使わなくても可)
個のテストケースが与えられるので、全てに答えてください。
Med:
Large:
Largeの制約では、最悪でも1分程度で答えが出る想定で、
Python、Javaでは1分以内に全ての答えが出力されることを確認しています。
解法
どの解法でも、1つ1つのテストケースに対して計算し、回繰り返すことを考えます。
Med
合計距離がとなるような組み合わせができるか(bool値)
として、
初期値:
遷移:
という計算をすると、で計算可能です。
個数制限無しナップサックを用いたもの。
Large
上記の解法だとメモリ、時間ともに足りません。
について全て調べたいですが、それぞれについて考えると、状態数が多くメモリと時間が足りなくなってしまうので、うまく状態数を減らしていくことを考えます(一般に、bool値のDPはより多くの情報を持たせて遷移させることができるはずです、例えば上記のbool値はできるかどうか、だけでなく、ほぼ同じ遷移で使用するはしごの最小個数をそのまま持たせることができます)。
とします。
距離のはしごのみを適切な回数用いることで、の倍数については必ず達成できることは明らかです。
の倍数ではないときも、ある距離が達成できた場合に、その後に距離のはしごを繰り返し使うことで、以上の、 距離をで割った余りがとなる地点は全て達成できます。
なので、距離をで割った余りそれぞれについて、可能な最小値を探せば、その最小値未満の値ができない、ということになります。ということで、
- がになるような合計距離の中で、作成可能な最小値
を考えていきます。
で初期化をします。
より小さい値の個数をそれぞれのについて計算してあげると、その総和が答えです。ここはです。
1つめ:01ナップサックに帰着
先ほどのdpについて、個数制限のないナップサック問題を適用することについて考えてみます。
という遷移になります。
,dp配列がであったときを考えます。
普通、個数制限無しナップサックを考える場合は、dpの添え字0から順番に増やしていきますが、例えばだった場合、
次の遷移では
になります。しかし、添え字5から始めると、
になります。
(です)。
このように、始まる添え字によって値が変わり、うまくDPを行うことができません。
このことは、個数制限なし、という条件を、
距離のはしごの1個セット、2個セット、4個セット....を用意してあげて、
距離のはしご個セットを使う/使わない
という2択にすることで、2進数で任意の個数を表現しつつ、うまく処理することができます。
- 個目のはしごセットについて使用する/しないを決めたとき、 がになるような合計距離の中で、作成可能な最小値
とすると、j個目のはしごセットの距離の総和をとして、
と遷移することができます。
はしごセットは、それぞれのはしごについて、となるについて考えればよいので、種類で抑えられます(実は、で抑えることもできます) 。
よって、で解けました。
2つめ:個数制限なしナップサック
1つめの解法の、個数制限なしナップサックに戻ってみます。
先ほど上手くいかなかったのは、添え字から、順番にずつ増やしていたからです。
ここを賢く走査することで、個数制限なしナップサックでも解けるようにしてしまいます。
種類目のはしごについて、dpの添え字の遷移は、本のループにわかれます。
例えば、,のとき、
というループと、
というループにわかれます。
1ずつ添え字を増やして見ていくのではなく、このそれぞれのループについて、個数制限なしナップサックを考えます。
このそれぞれのループについて、現在の最小値を探します。すると、その最小値の部分は、他がどうであれ、今見ている長さのはしごをどれだけ使っても、この最小値が更新されることはありません。
なので、この最小値のインデックスからスタートして、
と見ていけば、ループを1周するだけでうまく更新できます。
最小値を探すのと、更新それぞれで1周ずつすればよく、それぞれのループにある添え字の個数の合計は結局になるため、はしご一種類あたりで計算できます(最小値を探さなくとも、適当な場所から更新をはじめ、2周する、という方法でもいいです)。
これを回繰り返すため、でこの問題が解けました。
3つめ:ダイクストラ法
- がになるような合計距離の中で、作成可能な最小値
に戻ります。
現在の距離がの際に、はしごを新たに用いたとすると、
距離はに、 は になります。
について、添え字を頂点番号と考え、頂点ではしごを使う操作を、コスト、遷移先の頂点が の辺としたグラフを考えることができ、
それぞれの頂点に行く、最短距離を求める問題としてみることができます。
最短距離といえばダイクストラ法です。
から、順にはしごを使う操作による遷移を行い、を埋めることをダイクストラ法を用いて行うと、
頂点数が、辺の本数が本なので、
で解くことができます。
おまけ(Med解以外の想定TLE,MLE解法)
実は、距離が十分に大きい時、
~ すべてについての最大公約数をとすると、
の倍数は全て構築可能、そうでない場合は構築不可能になります。
距離が十分に大きい、というのは、以上であればOKです。
つまり、までは愚直にMed解法と同じDPで見て、それより大きい部分については、の倍数でない数を数えるだけで、この問題に対して答えることができます。です。
コード
解法1,2についてはこちらにアップしました(2020/05/19)。
main.c
が解法1つめ、mainver2.c
が解法2つめです。
AOJ 2335 - 10歳の動的計画
解法
縦方向が、横方向がとします(多分問題と逆…?)
移動は必ず全体で回になります。
縦に回、横に回寄り道すると決めた時、上記の移動のうち、縦方向に移動するのが回、横方向が回になるので、先にその方向を決め打つと、
通りになります。
あとは、縦横独立に考えることができるので、
を計算すれば、答えになります。
ここからは縦について、つまり元の長さがで、回寄り道することを考えます。横方向についても同様に計算できます。
座標が負になることを許容すると、全体で通りになります。
ここから、
- 回目に寄り道をした結果、初めて座標が負になる移動の仕方
をについて計算し、全体から引けば、求めたい移動パターン数になります。
回目に寄り道をして、初めて座標が負になるには、
を満たしながら、
正負ともに回ずつ移動し、その後負に移動する、という道筋をたどるしかありません。
これは、番目のカタラン数をと表記すると、です。
そして、その後は自由に移動できるので、通りあります。
よって、回目に寄り道をした結果初めて負になる場合の移動パターン数は、
であり、求めたかった、縦の移動パターン数は、
となります。
あとは、これを最初の方で求めた式に当てはめれば、答えが求まります。
AOJ 2293 - Dangerous Tower
解法
全て含めて、横の長さと箱が1:1に対応します。
なので、横の長さと箱をマッチングさせていくことを考えます。
箱について、
とマッチングさせる
とマッチングさせる
マッチングさせない
という3つの選択肢が存在します。
それぞれの場合について、高さがどれだけ増えるか、ということを考えると、
とマッチングさせる
→増加とマッチングさせる
→増加マッチングさせない
→増加なし
となります。
これを、コストに置き換えます。
はじめ、だけポイントを貰っていたとすると、それぞれの選択によるコストは、
とマッチングさせる
→のコストがかかるとマッチングさせる
→のコストがかかるマッチングさせない
→のコストがかかる
となります。
あとは、それぞれの箱をマッチングさせ、最小コストになるようにすればよいです。
これを求めるのに最小費用流を用います。
超頂点、箱を表す個の頂点、横の長さを表す頂点を用意します(横の長さを表す頂点は、の種類が個数になります)。
から箱の頂点それぞれにコスト0の辺を張る
箱の頂点から、を表す頂点にコストの辺を張る
箱の頂点から、を表す頂点にコストの辺を張る
箱の頂点から、にコストの辺を張る
横の長さを表す頂点から、にコスト0の辺を張る
という操作を行います。容量は全て1です。
あとは、からに、だけフローを流すときの最小コストを求めれば、
から引くことで答えが求まります。
感想
これと似ているな、と思って解いたので初めてマッチング問題を自力で解けました。いいですね。