geam1113’s diary

主にAtcoderのコンテストの備忘録を書きます。AtCoder緑と水色を行ったり来たりしています。

ABC242D ABC Transform

解説AC

問題へのリンク
公式解説へのリンク

テキストとyoutubeの解説の自分用まとめです。特に新しいことはありません。公式を見た方が詳細です。

二分木として考える

各文字が2つの文字になることを順に図示すると、二分木の構成になります。

よって、この問題は、

S^{(0)}_0,\ S^{(0)}_1,\ \cdots S^{(0)}_{N-1}を根とする二分木における、深さtk番目の文字を求めよ

という問題と見ることができます。

mod\ 3における0,1,2をA,B,Cに対応させます。
Aの左子ノードがB、右子ノードがCであるとします。
左子ノードに進むことは、mod\ 3において+1すること、右子ノードに降りることは+2することと同じです。B,Cにおいても同様に考えることができます。

よって、親の文字がわかれば子の文字を求めることができます。

以降、+xするという表現はmod\ 3上での操作を指します。

親と子の番号の関係

深さtにおいて、k番目である場合、その子は、深さt+1において、左子ノードが2k番目、右子ノードが2k+1となります。

以下が理由です。
深さtにおいてk番目である場合、自分より前には0,...,k-1までのk個のノードが存在する。
深さt+1には、それらの子ノードが2個ずつ存在し、その数は2k個となる。それらの番号は、0,...,2k-1となるから。

これより、

  • 親がk番目なら、子の番号は左が2k、右が2k+1
  • 子の番号がkなら親の番号は、\lfloor \frac{k}{2} \rfloor

が言えます。
また、

  • 子の番号が偶数なら親の左子ノード、奇数なら右子ノード

も言えます。

tが小さい場合

解説にならって、S^{(t)}k番目の文字を求める関数をf(t,k)とおきます。

以下のように再帰的に求められます。

f(t-1,\lfloor \frac{k}{2} \rfloor )を求め、kが偶数なら+1、奇数なら+2します。
tが十分小さい場合、再帰の過程でt=0となります。この時、f(0,k')S^{(0)}_{k'}として返せばよいです。

tが大きい場合

深さが1増えると子の数は2倍になります。
深さdから、60回深いところに進むと、0,...,2_{60}-1番目の文字は全てS^{(d)}_0の子孫であることがわかります。

kの最大値は10^{18} \lt 2^{60}-1であることから、tから60回ほど親を遡ったところをt'とすれば、深さtk番目の文字は全てS^{(t')_0}の子孫となります。

ある深さdにおけるS^{(d)}_0は、全てS^{(0)}_0の左子ノードをd回進んだ文字であるので、S^{(0)}_0+dすることで求められます。

以上より、再帰においてkを1/2していくと、高々60回ほどで、k=0となります。このとき、f(t',0)S^{(0)}_0+t'として返すようにすればよいです。
提出コード(公式とほぼ同じ)

ABC241 参加記録

コンテスト中AC:A〜C,E
コンテスト後、Dは自力AC

2022/07/06追記
Eの実装に単射や同値類という記載がありますが、誤りです。 無視してください。

D - Sequence Query

実装が複雑になりましたが、自作双方向連結リストでACしました。

  • 出現した整数の昇順ソートされた順序集合S (set)
  • 初めて出現した整数が双方向連結リストに何番目に挿入されたかを記録するハッシュマップM (unordered_map)
  • 双方向連結リストD

を準備します。
それぞれにあらかじめ、-\infty , \inftyを入れておきます(番兵)。
Dには昇順に値が挿入されていくように処理します。

具体的には、i番目のクエリは以下のように処理します。

クエリ1 x

  1. xDに初めて挿入される場合、MDの何番目に挿入されたかを記録する。
  2. xより大きい最小の整数ySから二分探索で得る。
  3. yDの何番目に挿入されたかをMから得る。これをj番目とする。
  4. Dにおいて、j番目に挿入された値(y)の後ろにxを挿入する。

クエリ2 x k

  1. xより大きい最小の整数ySから二分探索で得る。
  2. yDの何番目に挿入されたかをMから得る。これをj番目とする。
  3. Dにおいて、j番目に挿入された値(y)から後ろにk回辿る。この時、途中または最後に-\inftyにたどり着いた場合、-1とする。そうでない場合、辿った先を答えとして出力する。

クエリ3 x k

  1. xより大きい最小の整数ySから二分探索で得る。
  2. yDの何番目に挿入されたかをMから得る。これをj番目とする。
  3. Dにおいて、j番目に挿入された値(y)から前にk-1回辿る。この時、途中または最後に\inftyにたどり着いた場合、-1とする。そうでない場合、辿った先を答えとして出力する。

multisetを使うとイテレータの増減だけでいいようです。今更ながら勉強になりました。

提出コード

E - Putting Candies

i\rightarrow (i+A_i)\ mod\ Nに移動すると考えます。
常に移動先は同じなので、鳩の巣原理によって、多くともN回くらい移動すると、同じ場所が2回現れ、その後ループします。

あとは、K回の移動で何回iから移動するかをカウントすれば答えを計算できます。

それを自作ライブラリ化してあったので、よければ参考にしてください。
実装はコンテスト後に少し修正してあります。

なお、この問題は各頂点が有向辺を1つだけもつグラフとも捉えられます。
そのようなグラフは閉路を1つだけ持ち、すべての頂点は閉路に属するか、閉路へ向かうように向きづけられています。
(ここで一応証明してみてあります。AtCoderの過去問の記事なのでネタバレ注意)

提出コード

ABC239F Construct Highway

解説AC

問題へのリンク
解説1へのリンク
解説2へのリンク

web解説を見てよくわからなかったので、自分なりに証明してみました。
素人なので、間違っていたらすみません。

森と、森の各連結成分C_iについて、他の連結成分と連結しなければならない次数の不足数D_iが与えられる(以下、不足数と表現します。)。これを満たすように連結させ、森を1つの木とすることを考えます。

C=1のときは既に木が構成されているので、C\geq 2を仮定します。

これを達成するための必要十分条件は、連結成分数をC、与えられた不足数の合計をDとして、

D=2C-2かつD_i\gt 0

が成り立つことである。 また、これを満たす時、その構成方法は、

C\geq 3なら、少なくとも一方が不足数2以上である連結成分を選び、連結する
C=2なら、不足数が1である2つの連結成分を連結にする

ことである。

まず、木を構成する上で、なぜ
C=2C-2かつD_i\gt0
という条件が出てくるのかメモしておきます。

木の構成可能条件が出てくる理由

Tの辺を1本ずつ切断していくことを考える。

任意の辺を1本切断した時、連結成分数はC_1,C_2の2個になり、不足数D_1 = D_2 = 1で合計は2となる。 これは、D=2C-2かつD_i\gt 0を満たす。

次に、k本目の切断時にD=2C-2かつD_i\gt 0が成り立っていると仮定し、k+1本目の辺を切断した時を考える。
辺が1本以上存在する任意の連結成分C_xの辺を切断し、C_y,C_zに分割されたとする。

C_y,C_zには次数の不足が+1ずつされることからD_y,D_z\geq 1 \gt 0が成り立つ。

k+1本目の辺を切断した後の連結成分数をC'不足数の合計をD'とすると、連結成分数は1つ増え、次数の不足は2つ増える。

k本目切断時のC,Dから、C' = C + 1D' = D + 2となるので、D=2C-2に代入すると、
D'-2=2(C'-1)-2\rightarrow D'=2C'-2
となる。
従って、D=2C-2が成り立つ。

以上から、木から任意の辺を1本以上取り除くと、D=2C-2かつD_i\gt 0が常に成り立つことが示された。

このようにして条件が出てきます。

次に、D=2C-2かつD_i\gt 0ならば、木が構成できることを構成方法の正当性とともに示します。

D=2C-2かつD_i\gt 0\Rightarrow木を構成できる

C\geq 3である間、先程示した帰納法の逆を考えると、任意の連結成分を連結しても常に、D=2C-2が成り立つ。

連結する2つの連結成分の内、少なくとも一方について、連結前の不足数が2以上であれば、不足数が各々1減っても新たにできる連結成分の不足数は0とならないので、D_i \gt 0も保たれる。

よって、C=2となるまで、この操作を繰り返す。

C=2となったとき、D=2C-2かつD\gt 0が成り立っていることから、2つの連結成分のそれぞれの不足数が1である。これを連結させることで1つの木とできる。

以上で証明は終了ですが、以下を示しておきます。

補足

C\geq 3であり、D=2C-2かつD\gt 0ならば、

  • D_i = 1しか存在しない
  • D_i\geq 2しか存在しない

ことがあり得ないことを示す。
これが示されると、各段階で常に不足数が2以上の連結成分と1の連結成分が1個以上存在することになる。 また、これが成り立たないと、構成の途中で木にできない状態を作り出してしまうことになる。

D_i = 1しか存在しない状態にならない

この状態になったと仮定すると、D=Cとなり、D=2C-2が成立していることから、C=2C-2\rightarrow C=2が成り立つはずである。 しかし、C\geq 3という仮定からこれは成り立たない。

D_i \geq 2しか存在しない状態にならない

D\geq 2Cとなるが、D=2C-2と矛盾する。

最後に、十分性を示しておきます。

木を構成できる\RightarrowD=2C-2かつD_i\gt 0

対偶

  • D\neq 2C-2であるか、または、不足数0となる連結成分が1つでも存在するなら木を構成できない

を示す。

不足数0である連結成分が1つでも存在すると、その連結成分を連結できないため、1つの木とならない。よって、木を構成できない。

i回目の連結操作の終了時点で、D\neq 2C-2が成立し、i+1回目の連結操作でD=2C-2にできると仮定する。
しかし、先程示した帰納法と同様の議論で、i+1回目にD=2C-2が成立する場合、i回目の操作終了時点にD=2C-2が成り立つことが証明できる。
よって、仮定と矛盾するため、D\neq 2C-2からD=2C-2が成立する状態にできない。
従って、D\neq 2C-2の状態から1つの木を構成することはできない。

以上で対偶が示された。

解法

これでweb解説の2つの構成方法

がそれぞれ正しいことがわかりました。

ところで、次の構成方法も問題ないはずです。

  • 初期状態(M個の辺を繋げた状態)において、不足数が最大のものをC_xとおく。まず、不足数が2以上の連結成分を全てC_xに連結させる。その後、不足数が1の連結成分をC_xに連結させる。

この解法でACできました。
(C_xとして不足数最大を取らなくても不足数2以上であれば何でも良さそうです)

なお、構成前の前提として、以下の場合も解なしです。

  • 頂点数と不足数の和がD=2N-2を満たさない
  • M個の辺を繋いだ時に不足数が負となる連結成分が存在する

余談ですが、解説にあるような、個数の小さい方から大きい方にマージすることをマージテクと呼ぶのを初めて知りました。

提出コード

ABC240 参加記録

コンテスト中AC:A〜E

E - Ranges on Tree

コンテスト中は確信が持てないまま通してしまいました。
解法に至った経緯をメモしておきます。

まず、問題文の意味がパッと見で理解できませんでした。そこで、入出力例を確認して、ざっくりと以下のような制約であると理解しました。

  • 親の区間を、子に分配する。この時、子の区間は親の区間をはみ出してはならず、かつ、子同士の区間が重なってはならない。

ここまでで解法は思いつかなかったので、問題の例を全て確認してみました。すると、以下がわかりました。

  • 根の区間は、葉の個数をkとして、[1,k]
  • 葉の区間は、ある正整数m=1,...,kをとって、[m,m]であり、mは葉同士で相異なる

ことがわかりました。
そこで、葉にそれぞれm=1,...,kまで割り振って、その葉の区間[m,m]とし、その後、親に向かうにつれて区間を併合していく解法を思いつき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で以下を記録しておきます。

  • 頂点u_iのpre-order順。これをId_uとします。
  • X_iを頂点のpre-order順に並べたもの。これを配列Aとします。
  • 部分木に属する頂点数。これをdp_uとします。

このようにすると、i番目のクエリは、

  • Id_{V_i} \leq j \lt Id_{V_i}+dp_{V_i}を満たすA_jのうちK_i番目に大きい数を求めよ

という問題となります。 入力例1を例にします。以下はDFS実行後の一例です。

A = \{1,4,2,5,3\}
 Id = \{1,3,5,2,4\}
 dp = \{5,3,1,1,1\}

ここで、2個目のクエリ、頂点2を根とした時の1番目に大きい数を求めてみます。
対象となるA_j区間は、3\leq j \lt 3+3 \rightarrow 3\leq j \lt 6となるので、A_3,A_4,A_5から1番目に大きい値を計算します。
2,3,5なので、答えは5です。

ここで、K番目に大きい値を高速に得る必要があります。
これはウェーブレット行列により可能となります。

Aのウェーブレット行列を求めておます。ウェーブレット行列には以下の関数があります。

  • quantile(s,e,r) : 半開区間[s,e)のうち、r+1番目に小さい値を返す。計算量はO(logX_{max})。(但し、X_{max}X_iの最大値)

これを用いると、区間のサイズがdp_{V_i}であることから、quantile(Id_{V_i},\ Id_{V_i}+dp_{V_i},\ dp_{V_i} - K)として解を得られます。

計算量は、木なのでDFSにO(N)かかります。また、ウェーブレット行列の構築にO(NlogX_{max})
ウェーブレット行列で各クエリに答えるのにO(QlogX_{max})かかります(多分)。

提出コード

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

A_1,\ A_2及びS_iが決定すると、i\geq 3A_iを決定できます。
また、A_i \geq 0という制約があるので、A_1,\ A_2に関する何らかの制約が得られそうです。
A_7まで、列挙してみます。
A_1 = A_1
A_2 = A_2
A_3 = S_1 - A_1 - A_2
A_4 = S_2 - S_1 + A_1
A_5 = S_3 - S_2 +  A_2
A_6 = S_4 - S_3 + S_1 - A_1 - A_2
A_7 = S_5 - S_4 + S_2 - S_1 + A_1

S_iのみからなる定数部分をB_iとしておきます。B_iは、

i \leq 2のとき、B_i = 0
 i \geq 3のとき、B_i \leftarrow S_{i-1} - B_{i-1} - B_{i-2}

として、計算できます。

次に、A_1,\ A_2に関する制約式を得たいので、それぞれのA_iでの係数をA_1 [i],\ A_2 [i]とします。
これも以下のように計算できます。

i=1のとき、A_1 [1] = 1,\ A_2 [1] = 0
i=2のとき、A_1 [2] = 0,\ A_2 [1] = 1
i\geq 3のとき、
A_1 [i] = -A_1 [i-1]- A_1 [i-2]
A_2 [i] = -A_2 [i-1]- A_2 [i-2]

これを基に実際に実装して計算してみたところ、以下の事実に辿り着きました。

i\ mod\ 3 = 0のとき、A_1 [i] = -1,\ A_2 [i] = -1
i\ mod\ 3 = 1のとき、A_1 [i] = 1,\ A_2 [i] = 0
i\ mod\ 3 = 2のとき、A_1 [i] = 0,\ A_2 [i] = 1

そのため、A_i \geq 0という制約から、以下の3種類の制約式が得られます。

i\ mod\ 3 = 0のとき、A_1 + A_2  \leq B_i
i\ mod\ 3 = 1のとき、A_1 \geq -B_i
i\ mod\ 3 = 2のとき、A_2\geq -B_i

そこで、
A_1+A_2の上限をX
A_1の下限をY
A_2の下限をZ

とします。初め、X=S_1,\ Y=Z=0です。
これをi=3,4,...,N+2について、以下のように更新していきます。

i\ mod\ 3 = 0のとき、X\leftarrow  min(X,\ B_i)
i\ mod\ 3 = 1のとき、Y \leftarrow max(Y,\  -B_i)
i\ mod\ 3 = 2のとき、Z \leftarrow max(Z,\  -B_i)

これが終了したとき、
0\leq A_1 + A_2 \leq X
Y\leq A_1 \leq S_1
Z\leq A_2 \leq S_1
という制約式が得られ、これを満たすようなA_1,\ A_2を設定します。

まず、
X\lt 0
Y\gt S_1
 Z\gt S_1
のいずれかを満たす時、解なしです。

そうでないとき、
0\leq A_1 + A_2 \leq X
を満たすようにするには、A_1,\ A_2は、それぞれの下限であるA_1=YA_2 =Zにしておくのが最善です。
そのため、Y+Z\gt Xであれば解なしです。

そうでなければ、A_1=YA_2 =ZとしてAを構築できます。
(下記実装はiが0-indexedなので、i mod 3の値が異なっています) 提出コード

C - XOR to All

A=\{A_1,\ A_2,\ A_3,\ A_4,\ A_5\}として以下に一例を示します。

  1. A_1と全体のXORをとる。 \{A_1\oplus A_1,\ A_2\oplus A_1,\ A_3\oplus A_1,\ A_4\oplus A_1,\ A_5\oplus A_1\}

  2. A_5\oplus A_1と全体のXORをとる。
    \{A_1\oplus A_1\oplus A_5\oplus A_1,\ A_2\oplus A_1\oplus A_5\oplus A_1,\ A_3\oplus A_1 \oplus A_5\oplus A_1,\ A_4\oplus A_1\oplus A_5\oplus A_1,\ A_5\oplus A_1\oplus A_5\oplus A_1\}
    XORは交換法則が成り立ち、かつ、 X\oplus Y\oplus Y = X\oplus 0 = Xより、
    \{A_1\oplus A_5,\ A_2\oplus A_5,\ A_3\oplus A_5,\ A_4\oplus A_5,\ A_5\oplus A_5\}

これより、任意に置き換えを行った場合でも最終的には、
\{A_1\oplus A_i,\ A_2\oplus A_i,\ \cdots,\ A_N\oplus A_i\}
という形になります。

そこで、i=1,2,...,Nについて、
B_i=(A_1\oplus A_i)+(A_2\oplus A_i)+\cdots +(A_N\oplus A_i)
とした時、最大のB_iを求める問題と読み替えられます。

愚直にこれを計算するとO(N^2)となって、間に合わないため、工夫する必要があります。

ビットの問題では各ビットに着目するのが典型なので、それを基に考えてみます。すると、

  • A_ijビット目が0の場合、A_1,\  A_2,\ \cdots ,\ A_Njビット目はそのまま。
  • A_ijビット目が1の場合、A_1,\  A_2,\ \cdots ,\ A_Njビット目はすべて反転する。

ということがわかります。
そのため、A_1,\  A_2,\ \cdots ,\ A_Nのうち、jビット目が1の個数(または0の個数)さえ分かれば、以下のようにB_iを計算できます。

cnt_jA_1,\  A_2,\ \cdots ,\ A_Nの内、jビット目が1である要素の個数とし、
A_iの最大値A_{max}の最上位ビットをDビット目とする。

j=0,1,2,...,Dについて、

  • A_ijビット目が0の場合

    B_i2^jcnt_j個分足す:B_i \leftarrow B_i + cnt_j \times 2^j

  • A_ijビット目が1の場合

    B_i2^jN-cnt_j個分足す。:B_i \leftarrow B_i + (N-cnt_j) \times 2^j

このようにすると、全てのB_iO(DN)で計算できます。 A_i \lt 2^{30}という制約上、Dは最大で29なので、jのループは最大でも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になっているので問題ないということでした。

提出コード