ABC244 参加記録
コンテスト中AC:A〜E
C - Yamanote Line Game
D - Swap Hats
自信ないまま通してしまいました。
操作を偶数回行った後に生成される状態には回行った後もその状態にできます。
10回操作すると状態数はになるものの、RGBの順番の入れ替えの通り数は6通りしかありません。
そのため、を10回操作してにできれば達成できると考えました。
実際に実装してデバッグしてみると、状態数は偶数回、奇数回共に早々に3個に限定され、解を得られそうだとわかりました。
そして、提出した結果ACできました。
公式解説によれば、6種類の状態の関係性をグラフにすると、二部グラフになり、が同一のグループになれば達成できるということでした。
E - King Bombee
パスに関する以下のDPを考えます。
回移動した後に頂点にいるようなパスの総数
初期値は
これは、のうち、であるものの総数に一致します。
このDPテーブルの更新は、以下のような3重ループになります。
for k = 1からKまで for u in 全ての頂点 for v in uと接続する全ての頂点 dp[v][k] = dp[u][k-1] //uからvに移動する
for v in uと接続する全ての頂点
の部分は、のループの1回につき、合計で回しか実行されないので、になります。
また、回目でにいるパスの総数はとなります。
ここまでは、同様の考え方が過去に出題されました。
あとはに訪れた回数(に含まれるの個数)の偶奇の情報が欲しいので、DPテーブルに加えます。
回移動した後に頂点にいて、かつに訪れた回数の偶奇がであるようなパスの総数
但し、であり、の時はに訪れた回数が偶数、の時は奇数
DPテーブルの更新は、以下のようにの時だけ、偶奇を入れ替えればよいです。
for k = 1からKまで for u in 全ての頂点 for v in uと接続する全ての頂点 if vがX dp[v][k][0] = dp[u][k-1][1] dp[v][k][1] = dp[u][k-1][0] else dp[v][k][0] = dp[u][k-1][0] dp[v][k][1] = dp[u][k-1][1]
答えはです。
提出コード
ARC137 参加記録
コンテスト中AC:なし
A,B解説AC
A - Coprime Pair
素数の間隔(prime gap)が重要とのことです。
公式解説及び上記のwikiによれば、問題の制約における素数間の極大の間隔(maximum gap)は1500らしいです。
よって、及びを満たす組のうち少なくとも1つはがいずれも素数である組が存在するということでした。
とすると、の範囲だけとなる組を調べれば良くなり、全探索可能になるということでした。
B - Count 1's
- コンテスト中にたどり着いた部分
連続した部分列の0の個数を、1の個数を、初期状態における1の個数をとすると、操作後の1の数をにできます。
また、の最大値をとし、その時の連続した部分列の区間を閉区間とします。
区間が空の状態から、+1ずつ伸長してにした時、も+1ずつしかされないので、にできます。
最小値についても同様です。
従って、は、を満たす全ての整数にすることができます。
以上から、に0を含むならが答えとなります。
0を含まないなら、空の部分列を選ぶと0にもできるので、となります。
- コンテスト中わからなかった部分
の求め方がわかりませんでした。
公式解説によれば簡単に求められるとなっていました。
閉区間の部分列に含まれる0の個数を、1の個数をとします。
に含まれる0の個数は、1の個数はです。
よって、のは以下のように計算できます。
従って、を末尾とする連続した部分列のが最大となるのは、を満たすのうち、が最小となるもの選んだ場合です(便宜上、 )。
同様に、が最小となるのは、が最大となるもの選んだ場合です。
よって、について、を順次計算し、最大値と最小値を管理しておくことで、で各を末尾とした時の連続した部分列のの最大・最小を求められます。
以上から、をで求められます。
ABC242G Range Pairing Query
解説AC
初Mo's Algotithm
Mo's Algorithmの適用可能条件は以下の通りです。
- 配列が不変
- クエリ先読み可
- 区間を+1, -1ずつ伸縮しても値の再計算が容易
1,2は満たすので3が満たせればよく、以下の方法で容易に計算できることがわかります。
:区間内のの出現数
とします。最大のペア数は、以下のように計算できます。
よって、Mo's Algorithmによりで解けます。
Mo's Algorithmは公式解説のリンクなど見れば良いですが、自分でも別記事に簡単にまとめました。
Mo's Algorithm
Mo's Algorithm
AtCoder Biginner Contestに出題されたので、実装してみました。
解説へのリンク(ネタバレ注意)
解説に書かれている以下のリンクに詳細があるので、アルゴリズムについては要約して書きます。
概要
数列についての個の閉区間のクエリにで答える。
やっていることはクエリの平方分割。
条件
- は不変
- クエリ先読み可
- から区間を伸縮しても、値を簡単に計算できる
アルゴリズム
- ブロック番号の昇順、同ブロック内ではの昇順でクエリをソートする
- ソート済みのクエリを左から順に区間を伸縮して値を計算していく
計算量
- 左端はほとんどがブロック内での移動なので、回
- 右端は同ブロック内で高々回の移動なので、回
- 上記を併せて
例
のサイズを9とします。
ブロックサイズ
与えられるクエリ
[5,7]
[1,3]
[3,5]
[4,4]
[7,8]
[2,5]
[0,8]
[7,7]
まず、クエリをソートします。
が属するブロックを第一キーに、を第二キーとして昇順にソートします。
が属するブロックはにより求められます。
ソート後(ブロック毎に区切ってあります。)
[1,3]
[2,5]
[0,8]
[4,4]
[3,5]
[5,7]
[7,7]
[7,8]
ソート後に順にクエリを処理します。
例えば、予め[2,5]の値は計算されていると仮定して[2,5] -> [0,8]を計算するときは、
- 右端を、[2,5] -> [2,6] -> [2,7] -> [2,8]という風に、+1ずつ区間を右に伸長しながら値を更新する
- 左端を、[2,5] -> [1,5] -> [0,5]という風に、-1ずつ区間を左に伸長しながら値を更新する
という感じです。
ABC243 参加記録
コンテスト中AC:A〜D
D - Moves on Binary Tree
公式解説の解法1とほぼ同じでした。
まず、根からへの移動方法の文字列を得ます。具体的には、
が1となるまで以下を行う。
の末尾に、が偶数なら
'L'
を、奇数なら'R'
を追加。
と更新。上記が終了したら、を反転させる。
その後、について、以下を行います。
が
'U'
なら、の末尾から文字を一つ取り除く。
そうでないなら、をの末尾に追加。
これによって、は根から答えのノードまでの移動方法の文字列となります。あとはこれに従って、根(番号1)から計算していけば良いです。
計算量は、までの文字列の大きさがで、最終的なの大きさもを超えないことから、だと思います。
書いていて気づきましたが、最終的には'U'
を含まないので、答えの番号を求めるときの分岐は不要でした。
解法2の考え方も思いついたのですが、ビットシフトしなければいけないと思い断念しました。
単純に末尾に末尾への追加・削除だけで良いことを思いつけなかったです。
ABC242D ABC Transform
解説AC
テキストとyoutubeの解説の自分用まとめです。特に新しいことはありません。公式を見た方が詳細です。
二分木として考える
各文字が2つの文字になることを順に図示すると、二分木の構成になります。
よって、この問題は、
を根とする二分木における、深さで番目の文字を求めよ
という問題と見ることができます。
における0,1,2をA,B,Cに対応させます。
Aの左子ノードがB、右子ノードがCであるとします。
左子ノードに進むことは、において+1すること、右子ノードに降りることは+2することと同じです。B,Cにおいても同様に考えることができます。
よって、親の文字がわかれば子の文字を求めることができます。
以降、+xするという表現は上での操作を指します。
親と子の番号の関係
深さにおいて、番目である場合、その子は、深さにおいて、左子ノードが番目、右子ノードがとなります。
以下が理由です。
深さにおいて番目である場合、自分より前にはまでの個のノードが存在する。
深さには、それらの子ノードが2個ずつ存在し、その数は個となる。それらの番号は、となるから。
これより、
- 親が番目なら、子の番号は左が、右が
- 子の番号がなら親の番号は、
が言えます。
また、
- 子の番号が偶数なら親の左子ノード、奇数なら右子ノード
も言えます。
が小さい場合
解説にならって、の番目の文字を求める関数をとおきます。
以下のように再帰的に求められます。
を求め、が偶数なら+1、奇数なら+2します。
が十分小さい場合、再帰の過程でとなります。この時、をとして返せばよいです。
が大きい場合
深さが1増えると子の数は2倍になります。
深さから、60回深いところに進むと、番目の文字は全ての子孫であることがわかります。
の最大値はであることから、から60回ほど親を遡ったところをとすれば、深さの番目の文字は全ての子孫となります。
ある深さにおけるは、全ての左子ノードを回進んだ文字であるので、することで求められます。
以上より、再帰においてを1/2していくと、高々60回ほどで、となります。このとき、をとして返すようにすればよいです。
提出コード(公式とほぼ同じ)
ABC241 参加記録
コンテスト中AC:A〜C,E
コンテスト後、Dは自力AC
2022/07/06追記
Eの実装に単射や同値類という記載がありますが、誤りです。
無視してください。
D - Sequence Query
実装が複雑になりましたが、自作双方向連結リストでACしました。
- 出現した整数の昇順ソートされた順序集合 (set)
- 初めて出現した整数が双方向連結リストに何番目に挿入されたかを記録するハッシュマップ (unordered_map)
- 双方向連結リスト
を準備します。
それぞれにあらかじめ、を入れておきます(番兵)。
には昇順に値が挿入されていくように処理します。
具体的には、番目のクエリは以下のように処理します。
クエリ1 x
- がに初めて挿入される場合、にの何番目に挿入されたかを記録する。
- より大きい最小の整数をから二分探索で得る。
- がの何番目に挿入されたかをから得る。これを番目とする。
- において、番目に挿入された値()の後ろにを挿入する。
クエリ2 x k
- より大きい最小の整数をから二分探索で得る。
- がの何番目に挿入されたかをから得る。これを番目とする。
- において、番目に挿入された値()から後ろに回辿る。この時、途中または最後ににたどり着いた場合、-1とする。そうでない場合、辿った先を答えとして出力する。
クエリ3 x k
- より大きい最小の整数をから二分探索で得る。
- がの何番目に挿入されたかをから得る。これを番目とする。
- において、番目に挿入された値()から前に回辿る。この時、途中または最後ににたどり着いた場合、-1とする。そうでない場合、辿った先を答えとして出力する。
multisetを使うとイテレータの増減だけでいいようです。今更ながら勉強になりました。
E - Putting Candies
に移動すると考えます。
常に移動先は同じなので、鳩の巣原理によって、多くとも回くらい移動すると、同じ場所が2回現れ、その後ループします。
あとは、回の移動で何回iから移動するかをカウントすれば答えを計算できます。
それを自作ライブラリ化してあったので、よければ参考にしてください。
実装はコンテスト後に少し修正してあります。
なお、この問題は各頂点が有向辺を1つだけもつグラフとも捉えられます。
そのようなグラフは閉路を1つだけ持ち、すべての頂点は閉路に属するか、閉路へ向かうように向きづけられています。
(ここで一応証明してみてあります。AtCoderの過去問の記事なのでネタバレ注意)