AtCoder Beginner Contest 301 メモ
コンテストページ: パナソニックグループプログラミングコンテスト2023(AtCoder Beginner Contest 301) - AtCoder
コンテスト中AC: A〜C
コンテスト後にD,Eを自力AC
D - Bitmask
以下の説明で、の最大値を十分に超えるため、最上位ビットを63としておきます。がこれより小さい場合はサイズが63になるように0を追加しておきます。
上位のビットから?のビットを決定していくことを考えます。上位ビットをできるだけ大きくした方が、値として大きくできるため、優先します。
この時、より小さい数にするために、?を使うのは高々1箇所であると言えます。なぜなら、一度未満となれば、その後の下位ビットをどのようにしたとしても、以上となることはありえないからです。
仮に番目のビットでが未満であることが確定した場合に最適解であったとします。この時、最上位ビットから番目までのビットはとで一致していることが言えます。
もし一致していなければ、番目より上位のビットでが未満であるか、より大きいかのいずれかとなってしまいます。
また、番目以降の?は全て1にするのが最適です。
を未満にするというのは、のビット目が1であり、かつ、のビット目が?である状況で、?を0にすることと言えます。
そこで、の?を1つずつ0にした場合について、その時のの最大値を全探索します。この時、
の最上位ビットからビット目までについて、?の箇所を全てと同一にし、以降は0であるような値
の0ビット目からビット目までについて、?の箇所を全て1とし、ビット目以降は0であるような値
となるように配列を作っておくと、ビット目で未満となった時の値をで計算できます。但し、便宜上、とします。(ビットを1オリジンで実装すれば-1が0になって実装しやすくなります。)
解の最大値の更新は、この値が以下である場合のみ行えばよいです。
テーブルはで構築でき、全探索もでできます。
なお、未満となる箇所が0箇所の場合も考慮する必要があります。これは、と同じです。
実装: Submission #41405709 - Panasonic Programming Contest 2023(AtCoder Beginner Contest 301)
E - Pac-Takahashi
同じお菓子を2回食べられないので、既に食べたお菓子の状態を冪集合で管理したいです。状態数分のマスを拡張したBFSはおそらく計算量的に厳しいです。
考慮すべきは、スタート-お菓子、お菓子-お菓子、お菓子-ゴール間の最短距離のみです。また、後は各状態に対する移動距離の最小値が求められればよいです。ここで思いつくのが巡回セールスマン問題です。
お菓子を頂点とみなします。また、計算量は定数倍落ちますが、実装の簡便さからスタートとゴールも頂点とみなします。
これら頂点に対する巡回セールスマン問題を考えると、状態で頂点にいる場合の最短距離を、頂点数をとしてで求めることができます。
最短距離を求めた後は、ゴールの頂点にいるdp値について、以下か調べます。以下であれば、冪集合の1の数をカウントし、更にそこからスタートとゴールの頂点数2を引いた値で、答えの最大値を更新すればよいです。
全点対間最短経路は各頂点からBFSすることで、で求められます。
実装: Submission #41471658 - Panasonic Programming Contest 2023(AtCoder Beginner Contest 301)
なお、詳細は割愛しますが、スタートとゴールは巡回セールスマン問題を解く時の頂点から除いて考えることができ、定数倍軽くなります。但し、例外処理などに気をつける必要があります。
実装: Submission #41471658 - Panasonic Programming Contest 2023(AtCoder Beginner Contest 301)
AtCoder Beginner Contest 299 メモ
コンテストページ: Tokio Marine & Nichido Fire Insurance Programming Contest 2023(AtCoder Beginner Contest 299) - AtCoder
コンテスト中AC: A〜E
D - Find by Query
20回以下の指定なので、log時間の方法を疑います。すなわち、二分探索です。
最初、とします。最初、は、0......1です。
とします。 の場合、0...0...1という感じです。とすると、は、0......1となります。
の場合、0...1...1という感じです。とすると、は、0......1となります。
このように二分探索を繰り返すと、常には、0......1という状態が保たれます。となるまでこれを繰り返し、をとすれば、答えの条件を満たします。
E - Nearest Black Vertex
問題文中の条件である、
- 全てのについて、との距離がちょうどであるような頂点の内のいずれかを黒に塗る
について、どの頂点を黒にすればよいか決めることは困難です。しかし、白に塗る頂点は以下のように確定します。
- 全てのについて、との距離が未満であるような頂点は全て白で塗る
これにより、全てのについて、黒に塗った頂点との距離が最小のものが未満とならないことが保証されます。
白で塗った頂点以外は、何色で塗ってもよいです。そのため、条件を満たすために黒で塗るのが最適です。
これで白黒の塗り方が決まりました。後は、問題文の条件を満たすか確かめれば良いです。
塗り方の決定及び条件を満たすかの確認は全点対間最短経路が分かっていればよく、Warshall-Floyd法では間に合わないので、全頂点を始点としてBFSを行えばよいです。計算量はだと思います。
AtCoder Beginner Contest 298 メモ
コンテストページ: TOYOTA MOTOR CORPORATION Programming Contest 2023#1 (AtCoder Beginner Contest 298) - AtCoder
コンテスト中AC: A〜F
E - Unfair Sugoroku
期待値や確率の問題はメモ化再帰で解ける場合が多いです。
状態遷移を考えた時、
遷移する確率×遷移先で得られた確率
の総和により答えを計算できます。具体的には、
高橋くんがマス、青木くんがマスで、手番の状態がの時に高橋くんが勝つ確率
但し、であり、のときは高橋くんの手番、のときは青木くんの手番とします。
からは以下のように遷移します。
の時
の時
今回遷移する確率は等確率なので、
の時
の時
です。
再帰の終了条件は、なら高橋くんの勝ちなので、なら青木くんの勝ちなのでです。
後は、メモ化再帰して、を解けばよいです。
計算量は、ワーストケースで状態数がで、各状態につき遷移にかかるので、であり問題ありません。
F - Rook Score
行のスコアの最大値+列のスコアの最大値を答えとできれば単純ですが、このようにはいきません。なぜなら、行と列を選んだ時、マスの整数が2回足されるため、この整数を引かなければいけないためです。
計算量的に選ぶ行を全探索することは可能です。後は、行を固定したときの列のスコアの最大値が高速にわかれば解くことができます。
やや大雑把ですが、例とともに解法を説明します。
行を選んだとします。
列の整数の和がであるような数列をとします。また、行には列に整数が書かれていたとします。
今求めたいのは、元のから同じ行の整数を引いた
です。
列の座標圧縮をします。仮に、
と変換されたとします。求めたいものは、
となります。
座標圧縮後のは以下なので、配列として保持できるサイズとなります。そのため、Segment Treeにより、区間最大のクエリに答えることで最大値を得ることが可能です。
後は、同じ行に存在する整数を引く部分ですが、これは各行毎に以下のように処理します。
- 同じ行の整数をから引く
- 最大値を得る
- 同じ行の整数をに足し、元に戻す
各整数につき、引く・足すが1回ずつしか行われないので、Segment Treeの値更新を考えてもで処理できます。
AtCoder Beginner Contest 297 メモ
コンテストページ: AtCoder Beginner Contest 297 - AtCoder
コンテスト中AC: A〜D
コンテスト後にE,Fを自力AC
D - Count Subtractions
基本的な動きはユークリッドの互除法と同じなので、その通りにすれば概ね問題ないですが、一点注意が必要です。
の大きい方を、小さい方をとします。がの倍数となった時、ユークリッドの互除法ではが0となるまでを引きますが、今回の問題はとなった時点で終了するようにしなければいけません。
実装はコンテスト後に見直し
実装: Submission #40555022 - AtCoder Beginner Contest 297
E - Kth Takoyaki Set
個数制限の無い部分和問題の解き方をベースにして解けます。
まず、の要素をただ1つ使う場合の部分和問題を考えます。
以下のDPテーブルを考えます。
の番目までを使ってが作れるなら1、作れないなら0
からへの配るDPでの遷移は、
- が1ならを1にする
- が1ならを1にする
です。
次に、個数制限がない場合の部分和問題を考えます。
上記の遷移に対し、以下を追加します。
- が1ならを1にする
この遷移を追加すると、
が1ならが1になる
が1ならが1になる
が1ならが1になる
...
となるので、番目のが1なら、任意の自然数に対して、番目のは全て1になり、個数が無制限である状況が作られます。
なお、の小さい方から順にテーブルを更新していく必要があります。
また、この配列は使い回すことができるので、
が作れるなら1、作れないなら0
として、のそれぞれについて、の順に、
- が1ならを1にする
をしていけばよいです。
番目の最大値が配列に持てる大きさならこのまま解けますが、今回は最大値がなので不可能です。
配列では作成できない値も管理しているため、無駄が多いです。
そこで、作成可能な値そのものを集合で管理するようにします。すると、集合には小さいほうから順に個だけ管理すればよいです(0除く)。
そこで、順序付き集合 (C++ならset)で作成可能な値の集合を管理できないか考えます。
前述のDPの遷移から以下の方法が考えられます。
- の要素の小さい順にを足した値をに挿入する
これだと、以下のような問題点が発生します。
- に存在する要素が小さい順に網羅されず、終了時点が不明
- がsetなら、要素の追加により木の構造が動的に変化するため、小さい順に走査するイテレータが正常に機能するか不明(確かめていませんが、問題なく機能するのかもしれません)
そこで、作成可能な値を小さい順に管理する集合を追加し、以下のようにします。
として、に値が個追加されるまで、以下を繰り返す
から最小値を取り出しとする(はから削除する)。 をに挿入し、をに追加する
このようにすると、にはの要素及びで作成可能な値が小さい順に個の記録されます。
また、繰り返し部分はとなります。
これで、で問題を解くことができました。
実装: Submission #40502322 - AtCoder Beginner Contest 297
F - Minimum Bounding Box 2
包除原理で解けます。
長方形の縦の長さと横の長さを全探索してもなので、全探索可能です。
縦、横、面積の長方形がグリッド上にできる方法が何通りか考えていきます。
一旦、ができる位置は固定してができるマスの選び方が何通りあるか考えます。
まず、外のマスが選ばれた場合はができないので、以下、マスは全て内から選ばれるとします。
この時、ができる条件は以下のとおりです。
- の上下左右のそれぞれの辺について、1つ以上選ばれたマスが存在する
となります。
さて、これが成立する条件はかなり複雑です。辺上に選ばれるマスは何個あってもよく、頂点となるマスは2つの辺を共有していることなどが理由です。
そこで、とならない数を全体から引くことを考えます。
とならない条件は、
- の辺について1つ以上、選ばれたマスが0の辺が存在する
よって、以下の計算ができます。
となる通り数 =
内のマスが選ばれる全通り数
- 選ばれたマスが0の辺がちょうど1つの通り数
- 選ばれたマスが0の辺がちょうど2つの通り数
- 選ばれたマスが0の辺がちょうど3つの通り数
- 選ばれたマスが0の辺がちょうど4つの通り数
- 選ばれたマスが0の辺がちょうどつの通り数
というのは求めるのが難しいです。
一方で、
- 選ばれたマスが0の辺がつ以上の通り数
というのは、求めるのが容易です。
全てを記載すると長くなるので、の場合についてのみ示します。他も同様に求められます。
"選ばれたマスを0にする辺"に含まれるマスの個数をとします。残りの個のマスから個のマスを選ぶような、通りが、選ばない辺を決めた時の通り数です。
2つの辺の選び方は、(左、上)、(左、下)、(右、上)、(右、下)、(右、左)、(上、下)があります。
(左、上)、(左、下)、(右、上)、(右、下)は、です。
(右、左)は、です。
(上、下)は、です。
この6パターンを合計することで、選ばれたマスが0の辺が2つ以上の通り数を得ることができます。
についてこれらを計算すると、包除原理により、以下のようにとなる通り数を計算できます。
となる通り数 =
内のマスが選ばれる全通り数
- 選ばれたマスが0の辺が1つ以上の通り数
+ 選ばれたマスが0の辺が2つ以上の通り数
- 選ばれたマスが0の辺が3つ以上の通り数
+ 選ばれたマスが0の辺が4つ以上の通り数
なお、内のマスが選ばれる全通り数はです。
これにより、グリッド内で位置を固定した時に、縦、横の長方形ができる通り数が求まりました。これをとします。
グリッド内では、個配置できます。全て同様の計算ができるので、グリッド内でができる全通り数は、通りです。
また、のスコアはなので、グリッド上でできるによって得られる全スコアは、です。
これを全てのについて合計します。それを、グリッド全体から個選ぶ通り数で割ったものが求める期待値となります。
AtCoder Beginner Contest 296 メモ
コンテストページ: AtCoder Beginner Contest 296 - AtCoder
コンテスト中AC: A,B,C,(D),E (Dは嘘解法でした)
E - Transition Game
出次数1の有向グラフ上を移動すると考えることができます。出次数1のグラフは、「各連結成分について、閉路をただ1つだけ持ち、全ての頂点から有向辺を辿ると閉路に辿り着く」ことが言えます。
そこで、まず高橋くんが青木くんを閉路上の頂点に行かせたい場合を考えます。
頂点からなる閉路について、任意の頂点から移動を開始して辿り着いた順にと番号をつけます。
番号から開始して、回移動した場合に最後にいる頂点の番号は、で計算できます。
青木くんが移動回数を指定した場合を考えます。高橋くんが移動を開始する頂点番号を指定し、頂点に行かせたいとすると、
という合同式から、
をにすれば良いです。
このことから、閉路上の任意の頂点については、青木くんの指定に対して、高橋くんは青木くんを必ず行かせたい頂点に行かせることが可能です。よって、高橋くんの勝ちとなります。
閉路でない部分の頂点に行かせたい場合を考えます。例えば青木くんがをなどとすれば必ず閉路に行き着くため、高橋くんは開始の頂点をどのように指定しても勝つことはできず、青木くんが勝ちます。
以上から問題の答えは、各連結成分についての、閉路に属する頂点数の合計です。
閉路に属する頂点数が2個以上なら強連結成分分解で得ることができます。また、頂点数が1個の閉路、すなわち自己ループは、かどうかで判定できます。
ユーザ解説によれば、出次数1の有向グラフをfunctional graphと呼ぶらしいです。
AtCoder Beginner Contest 295 備忘録
コンテストページ: AtCoder Beginner Contest 295 - AtCoder
コンテスト中AC: A〜D
D - Three Days Ago
便宜上、が空文字として存在するものとします。なお、以下に示す解法で「文字」と表現したときには空文字は無視し、0,1,2,3,4,5,6,7,8,9を指すものとします。
問題文のが満たすべき条件を以下のように言い換えられます。
- の文字目から文字目までについて、すべての文字の個数が偶数である
更に以下のように言い換えられます。
- の「文字目から文字目」と、「文字目から文字目」について、全ての文字の個数の偶奇が等しい
ここで、10桁の2進数を定めます。
は、の文字目から文字目までに文字が偶数個含まれているなら、ビット目が0、奇数個なら1とします。
このようにすると、の条件は更に言い換えられます。
そこで、について、を計算し、その個数をカウントします。ここから選ばれる2つのインデックスの組は、問題文の条件を満たします。
よって、となるインデックスの個数を個とすれば、個が条件を満たします。従って、の全てについてその総和をとれば、答えとなります。
ここで、はのビット目のビットを反転させることでで計算可能です。
の記録にハッシュマップを使えば、で計算可能です(もしくは、の方が大きくなります)。
AtCoder Beginner Contest 292 備忘録
コンテストページ: AtCoder Beginner Contest 294 - AtCoder
コンテスト中AC: A〜E, G
G - Distance Queries on a Tree
値の一点更新が可能な、木上のパスクエリを処理するデータ構造があれば、そのまま適用して解けます。
今回は、Link-Cut Treeで解きました。
この問題では辺の追加や削除の処理は不要なので、敢えてLink-Cut Treeにする必要はないです。