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の過去問の記事なのでネタバレ注意)
ABC239F Construct Highway
解説AC
web解説を見てよくわからなかったので、自分なりに証明してみました。
素人なので、間違っていたらすみません。
森と、森の各連結成分について、他の連結成分と連結しなければならない次数の不足数が与えられる(以下、不足数と表現します。)。これを満たすように連結させ、森を1つの木とすることを考えます。
のときは既に木が構成されているので、を仮定します。
これを達成するための必要十分条件は、連結成分数を、与えられた不足数の合計をとして、
かつ
が成り立つことである。 また、これを満たす時、その構成方法は、
なら、少なくとも一方が不足数2以上である連結成分を選び、連結する
なら、不足数が1である2つの連結成分を連結にする
ことである。
まず、木を構成する上で、なぜ
かつ
という条件が出てくるのかメモしておきます。
木の構成可能条件が出てくる理由
木の辺を1本ずつ切断していくことを考える。
任意の辺を1本切断した時、連結成分数はの2個になり、不足数はで合計は2となる。 これは、かつを満たす。
次に、本目の切断時にかつが成り立っていると仮定し、本目の辺を切断した時を考える。
辺が1本以上存在する任意の連結成分の辺を切断し、に分割されたとする。
には次数の不足が+1ずつされることからが成り立つ。
本目の辺を切断した後の連結成分数を、不足数の合計をとすると、連結成分数は1つ増え、次数の不足は2つ増える。
本目切断時のから、、となるので、に代入すると、
となる。
従って、が成り立つ。
以上から、木から任意の辺を1本以上取り除くと、かつが常に成り立つことが示された。
このようにして条件が出てきます。
次に、かつならば、木が構成できることを構成方法の正当性とともに示します。
かつ木を構成できる
である間、先程示した帰納法の逆を考えると、任意の連結成分を連結しても常に、が成り立つ。
連結する2つの連結成分の内、少なくとも一方について、連結前の不足数が2以上であれば、不足数が各々1減っても新たにできる連結成分の不足数は0とならないので、も保たれる。
よって、となるまで、この操作を繰り返す。
となったとき、かつが成り立っていることから、2つの連結成分のそれぞれの不足数が1である。これを連結させることで1つの木とできる。
以上で証明は終了ですが、以下を示しておきます。
補足
であり、かつならば、
- しか存在しない
- しか存在しない
ことがあり得ないことを示す。
これが示されると、各段階で常に不足数が2以上の連結成分と1の連結成分が1個以上存在することになる。
また、これが成り立たないと、構成の途中で木にできない状態を作り出してしまうことになる。
しか存在しない状態にならない
この状態になったと仮定すると、となり、が成立していることから、が成り立つはずである。 しかし、という仮定からこれは成り立たない。
しか存在しない状態にならない
となるが、と矛盾する。
最後に、十分性を示しておきます。
木を構成できるかつ
対偶
- であるか、または、不足数0となる連結成分が1つでも存在するなら木を構成できない
を示す。
不足数0である連結成分が1つでも存在すると、その連結成分を連結できないため、1つの木とならない。よって、木を構成できない。
回目の連結操作の終了時点で、が成立し、回目の連結操作でにできると仮定する。
しかし、先程示した帰納法と同様の議論で、回目にが成立する場合、回目の操作終了時点にが成り立つことが証明できる。
よって、仮定と矛盾するため、からが成立する状態にできない。
従って、の状態から1つの木を構成することはできない。
以上で対偶が示された。
解法
これでweb解説の2つの構成方法
がそれぞれ正しいことがわかりました。
ところで、次の構成方法も問題ないはずです。
この解法でACできました。
(として不足数最大を取らなくても不足数2以上であれば何でも良さそうです)
なお、構成前の前提として、以下の場合も解なしです。
余談ですが、解説にあるような、個数の小さい方から大きい方にマージすることをマージテクと呼ぶのを初めて知りました。
ABC240 参加記録
コンテスト中AC:A〜E
E - Ranges on Tree
コンテスト中は確信が持てないまま通してしまいました。
解法に至った経緯をメモしておきます。
まず、問題文の意味がパッと見で理解できませんでした。そこで、入出力例を確認して、ざっくりと以下のような制約であると理解しました。
ここまでで解法は思いつかなかったので、問題の例を全て確認してみました。すると、以下がわかりました。
ことがわかりました。
そこで、葉にそれぞれまで割り振って、その葉の区間をとし、その後、親に向かうにつれて区間を併合していく解法を思いつきACできました。
公式解説によれば、この解法の正当性として、
- 葉には必ず異なる番号(区間)を振らなければならないので、k個の異なる整数が必要であり、答えはk以上になる。一方、先に示した方法なら、答えをkにできる。よって、この方法で得られる解は最小であり、解法の正当性が示された。
という感じでした。
ABC239 参加記録
コンテスト中AC:A〜E
B - Intenger Division
以前、このような自作関数を作っていました。
template<typename T> T Floor(T a, T b) { if ((a<0 && b<0) || (a>0 && b>0)) return a/b; else return (Abs(a)+Abs(b)-1)/Abs(b)*-1LL; }
これを使うことで、Floor(X,10LL)
で求められました。
提出コード
E - Subtree K-th Max
Kが小さいことから、公式解説の想定解を思いつきつつも、計算量の見積もりがよくわからなかったので、異なる解法で解きました。
オイラーツアーの部分木クエリと同じ考え方となります。
まず、一回のDFSで以下を記録しておきます。
- 頂点のpre-order順。これをとします。
- を頂点のpre-order順に並べたもの。これを配列とします。
- 部分木に属する頂点数。これをとします。
このようにすると、番目のクエリは、
- を満たすのうち番目に大きい数を求めよ
という問題となります。 入力例1を例にします。以下はDFS実行後の一例です。
ここで、2個目のクエリ、頂点2を根とした時の1番目に大きい数を求めてみます。
対象となるの区間は、となるので、から1番目に大きい値を計算します。
なので、答えはです。
ここで、番目に大きい値を高速に得る必要があります。
これはウェーブレット行列により可能となります。
のウェーブレット行列を求めておます。ウェーブレット行列には以下の関数があります。
- : 半開区間のうち、番目に小さい値を返す。計算量は。(但し、はの最大値)
これを用いると、区間のサイズがであることから、として解を得られます。
計算量は、木なのでDFSにかかります。また、ウェーブレット行列の構築に、
ウェーブレット行列で各クエリに答えるのにかかります(多分)。
ARC135 参加記録
コンテスト中AC:A,C
Bはコンテスト後自力AC。もう少しで解けそうでした。
A - Floor, Ceil - Decomposition
実際に10まで、xとfloor(x/2)×ceil(x/2)のどちらが大きいか手計算しました。
すると、
- x≦4なら、x≧floor(x/2)×ceil(x/2)
- x≧5なら、x<floor(x/2)×ceil(x/2)
となりました。大きめの数で試してもx<floor(x/2)×ceil(x/2)となり、その差は大きくなっていくので、厳密でないものの一般のxでもこれが成り立つと考えました。
よって、
- x≦4ならそのまま、そうでなければfloor(x/2)とceil(x/2)に置き換える。
という解法が成り立つと考えました。
実装ですが、この手の問題ではメモ化再帰を使える場合が多いです。
メモ化することで、xが偶数なら状態は1/2になりますし、厳密ではないものの、全状態数は十分に少ないと見積もりました。
実際に提出してみると、ACできました。
(最大の1018くらいは試しておけばよかったです。)
厳密でなかった部分は、前者は公式解説に証明がありました。後者も証明できるそうです。(具体的な証明はありませんでした。) 提出コード
B - Sum of Three Terms
及びが決定すると、のを決定できます。
また、という制約があるので、に関する何らかの制約が得られそうです。
まで、列挙してみます。
のみからなる定数部分をとしておきます。は、
のとき、
のとき、
として、計算できます。
次に、に関する制約式を得たいので、それぞれのでの係数をとします。
これも以下のように計算できます。
のとき、
のとき、
のとき、
これを基に実際に実装して計算してみたところ、以下の事実に辿り着きました。
のとき、
のとき、
のとき、
そのため、という制約から、以下の3種類の制約式が得られます。
のとき、
のとき、
のとき、
そこで、
の上限を
の下限を
の下限を
とします。初め、です。
これをについて、以下のように更新していきます。
のとき、
のとき、
のとき、
これが終了したとき、
という制約式が得られ、これを満たすようなを設定します。
まず、
のいずれかを満たす時、解なしです。
そうでないとき、
を満たすようにするには、は、それぞれの下限である、にしておくのが最善です。
そのため、であれば解なしです。
そうでなければ、、としてを構築できます。
(下記実装はiが0-indexedなので、i mod 3の値が異なっています)
提出コード
C - XOR to All
として以下に一例を示します。
と全体のXORをとる。
と全体のXORをとる。
XORは交換法則が成り立ち、かつ、 より、
これより、任意に置き換えを行った場合でも最終的には、
という形になります。
そこで、について、
とした時、最大のを求める問題と読み替えられます。
愚直にこれを計算するととなって、間に合わないため、工夫する必要があります。
ビットの問題では各ビットに着目するのが典型なので、それを基に考えてみます。すると、
- のビット目が0の場合、のビット目はそのまま。
- のビット目が1の場合、のビット目はすべて反転する。
ということがわかります。
そのため、のうち、ビット目が1の個数(または0の個数)さえ分かれば、以下のようにを計算できます。
をの内、ビット目が1である要素の個数とし、
の最大値の最上位ビットをビット目とする。
について、
のビット目が0の場合
にを個分足す:
のビット目が1の場合
にを個分足す。:
このようにすると、全てのをで計算できます。 という制約上、は最大で29なので、のループは最大でも30回となり、余裕を持って間に合います。
なお、操作を一度も行っていない場合も考慮する必要があります。
ABC237F |LIS| = 3
解説AC
公式解説で解法だけみて、実装は自力でしてみようと思ったのですが、これも上手くいきませんでした。結局、実装も解説のものを確認しました。
LISの長さが3未満の保持方法
未確定の部分はinf、今回ならM+1としておけばよいようです。
例えば、{2,5}というLISを持つ数列はdp[i][2][5][M+1]という感じになります。
dp遷移におけるループ
a1,a2,a3のループ部分は、単調増加にしたくなります。
イメージ
for a1 = 1 to M+1
for a2 = a1+1 to M+1
for a3 = a2+1 to M+1
しかし、このようにするとM+1を複数含む場合の遷移が起こらないため、だめでした。
そのため、ループ部分は、
for a1 = 1 to M+1
for a2 = 1 to M+1
for a3 = 1 to M+1
と、しておく必要があります。
dp[i][2][5][1] += dp[i-1][1][5][1]ような不正な更新が行われますが、これらは全て0になっているので問題ないということでした。