geam1113’s diary

主にAtcoderのコンテストの備忘録を書きます。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になっているので問題ないということでした。

提出コード

ABC234F Reordering

解説AC

問題へのリンク
公式解説

単純に部分列を得るだけなら部分列DPなどが考えられますが、今回はすべての並び替えを得る必要があるので適用できません。
重複を含む並び替えの計算方法から考えてみます。

a,b,c,\cdots ,zの選んだ個数をそれぞれA,B,C,\cdots , Zとします。この時の並び替えの方法は、

\frac{(A+B+C+\cdots +Z)!}{A!\cdot B! \cdot C! \cdot\ \cdots \ \cdot Z!}

です。

よって、各文字を何個選んだかという情報があれば、並び替えが何通りあるか得ることができます。
そこで、それぞれ何個選ぶかをDFSで全探索してみましたが、到底間に合いませんでした。

ここからは公式解説の方法に自分の補足を加えながら書いてみます。

実は、どの文字を何個選んだかという情報は不要で、合計で何個選んだかさえ分かれば、その時に選んだ文字数に対する文字列の並び方の総数を計算できるということでした。

具体例として、aが3個、bが3個ある場合に、a,bから合計4個選んだ時の並び替えの総数から、その後cから3個選んだ時の並び替えの総数への遷移を考えます。

  1. aを3個、bを1個選んでいた場合
    \frac{4!}{3!1!}\frac{7!}{3!1!3!}になります。
    よって、
     \frac{4!}{3!1!}\times {}_7C_4 = \frac{7!}{3!1!3!}です。

  2. aを2個、bを2個選んでいた場合
    \frac{4!}{2!2!}\frac{7!}{2!2!3!}になります。
    よって、
     \frac{4!}{2!2!}\times {}_7C_4 = \frac{7!}{2!2!3!}

  3. aを1個、bを3個選んでいた場合
    \frac{4!}{1!3!}\frac{7!}{1!3!3!}になります。
    よって、
     \frac{4!}{1!3!}\times {}_7C_4 = \frac{7!}{1!3!3!}

以上から、
(\frac{4!}{3!1!}+\frac{4!}{2!2!}+\frac{4!}{1!3!})\times {}_7C_4 = \frac{7!}{3!1!3!}+\frac{7!}{2!2!3!}+\frac{7!}{1!3!3!}
となります。

ここで、dpを考えます。a,b,c,...を1番目,2番目,3番目,...とします。
dp[i][j]:=i番目までの文字を考えたとき、これまで選んだ文字の合計個数がjの時の、並び替えによって得られる文字列の総数
と、定義します。

すると、

dp[2][4]=\frac{4!}{3!1!}+\frac{4!}{2!2!}+\frac{4!}{1!3!}
dp[3][7]= \frac{7!}{3!1!3!}+\frac{7!}{2!2!3!}+\frac{7!}{1!3!3!}

であることから、

dp[3][7]=dp[2][4]\times {}_7C_4

となって、bまでで文字を合計4個選んだ時の文字列の総数から、cまでで文字を合計7個選んだ時の文字列の総数を計算できました。

一般化すると、i-1番目までで文字を合計k個選んだ時の文字列の並び替えの総数から、i番目でj個選んだときの文字列の並び替えの総数は、

dp[i][j+k]\leftarrow dp[i-1][k]\times {}_{j+k}C_k

と計算できます。

計算量についてです。
解説の実装部分は読まずに実装してみました。

    dp[0][0] = 1;
    ll sum = 0;
    rep(i,1,27) {
        rep(j,C[i-1]+1) {
            rep(k,sum+1) {
                dp[i][k+j] += dp[i-1][k]*cn.nCr(k+j,k);
            }
        }
        sum += C[i-1];
    }

こちらのdpの遷移部分の計算量ですが、jのループは、i番目の文字に対して毎回Sの文字数分回されるのではなく、
1〜26番目の文字全体を通して、合計でSの文字数分回されるので、ワーストの見積もりで26×5000×5000という計算量になるわけではなく、実際にはもっと少ない計算量になると理解しました。

提出コード

ABC238E Range Sums

解説AC

問題へのリンク
公式解説

公式解説のメモ程度です。

aの累積和をbとおくと、与えられる情報は、
b_{r_i} - b_{l_i -1} = X
という風になり、

  • b_{r_i}b_{l_i -1} の一方が分かれば、もう一方がわかる

という関係になる。 この関係をグラフ化し、b_0b_Nが連結ならYes。

ここまでが公式解説の内容です。
連立方程式を順に解いていくことをイメージしました。

ただ、b_iにたどり着ければその値を求められるのは理解できますが、たどり着けないところも何か求める手段がありそうな気がして少し腑に落ちないところがあります。

話は変わりますが、この問題は、Xが実際の値が与えられた時に

  • b_{l_i-1}からb_{r_i}へのコストXの有向辺
  • b_{r_i}からb_{l_i-1}へのコスト-Xの有向辺

とすれば、実際にb_Nを求めることもできそうです。
また、このように考えると、牛ゲーの特殊バージョンと考えられるような感じもします。

別解として、以下のリンクのように吐き出し法で解かれている方がおり、公式解説より私的には納得できました。