hitoare日記

たまに書きます

F - Intervals on Tree(AtCoder Beginner Contest 173)

問題リンク

atcoder.jp


問題概要

\(N\)頂点の木が与えられる。各頂点には\(1,...,N\)の番号が振られている。

\(1 \leq L \leq R \leq N\)に対し

\(f(L, R)\) を、「番号\(L\)以上\(R\)以下の頂点と、両端の番号が\(L\)以上\(R\)以下の頂点であるような辺からなる部分グラフの連結成分の個数」と定義する。

\(\sum_{L=1}^N \sum_{R=1}^N f(L,R)\)を求めよ。

解法

 各\(L,R\)に対し、できる部分グラフは森(各連結成分が木であるグラフ)となる。

このグラフには次のような性質がある:

  • (連結成分の個数) = (頂点の個数) - (辺の本数)

これは各連結成分に対し(頂点の個数)-(辺の本数)=1であることからわかる。

よって、求める値は

\(L\)と\(R\)を動かした時の

(頂点の個数の合計) - (辺の本数の合計)

となる。

頂点の個数の合計は、\(i=1,...,N\)に対し

\(L \leq i \leq R\)となる\((L,R)\)の組の個数の和なので

\(\sum_{i=1}^N i \times (N-i+1)\)である。

辺の本数の合計は、辺\( (u,v)\) (ただし\( u < v\) )に対しそれを含む\((L,R)\)は

\(L \leq u\), \(v \leq R\)を満たすことが必要十分なので

\(\sum_{i=1}^{N-1} u_i \times (N-v_i+1)\)となる。

提出

https://atcoder.jp/contests/abc173/submissions/14996570

感想

 気づきさえすれば楽。

連結成分の個数は頂点・辺の個数で言い換えられるといい感じ。

F - Strivore(AtCoder Beginner Contest 171)

問題リンク

atcoder.jp


問題概要

英小文字からなる文字列\(S\)と自然数\(K\)が与えられる。

\(S\)に「好きな位置に好きな英小文字を1つ挿入する」という操作をちょうど\(K\)回繰り返して得られる文字列の総数は何通りか。

解法

\(S\)の文字数を\(N\)とおく。

求めるものは、「\((N+K)\)文字の文字列で、\(S\)を(連続とは限らない)部分列に持つものの総数」である。

これは次のように言い換えられる。

  • a~zの文字が書かれた26面体のサイコロがある。Sの先頭から見て、現在見ている文字と同じ文字が出たらそれを書いて次の文字に進む、という作業を\((N+K)\)回繰り返す(\(S\)が完成してもサイコロは振り続ける)。目の出方\(26^{N+K}\)通りのうち、\(S\)が完成するのは何通りか。

\(N \leqq i \leqq N+K\)とし、ちょうど\(i\)回目で\(S\)が完成する場合を考えると、

\(1...(i-1)\)回目ではちょうど\(N-1\)回当たりの目(見ている文字と一致する目)、\(i-N\)回外れの目が出て、

\(i\)回目では\(S\)の最後の文字が出て、

\((i+1)...(N+K)\)回目では任意の目が出ている。

よってこの場合の総数は \( {}_{i-1} C_{N-1} \times (26-1)^{i-N} \times 26^{N+K-i} \) 通りになる。

これを\(i\)を\(N\)から\((N+K)\)まで動かして足せば答えが出る。

また、「当たりの目がN回以上出る組み合わせの総数」と考えて

\(\sum_{i=N}^{N+K} ({}_{N+K} C_{i} \times (26-1)^{N+K-i} )\)としても答えが得られる。

提出 

https://atcoder.jp/contests/abc171/submissions/14552574

感想

昨日のAGCでも文字列の数え上げが2つ出たけど、どれも違う解法で良かった。

C - Shift(AtCoder Grand Contest 046)

問題リンク

問題概要

\(0\)と\(1\)からなる文字列\(S\)が与えられる。

「\(1 \leqq i < j \leqq|S|\)かつ\(S_i=0,S_j=1\)となる\(i\)と\(j\)の組を選び、\(S_j\)を削除して\(S_i\)の直前に挿入する」という操作を考える。

この操作を0回以上\(K\)回以下繰り返して得られる文字列の総数は何通りか。

解法

操作で文字列がどのように変化するかを考える。

たとえば

0000011111

0000101111

0000110111

1000011011

のように変化する。

この時「選んだ0の直前に挿入する」という条件は、「選んだ0より前の任意の位置に挿入する」としてよい。

これは1が連続している場所にはそのどこに挿入しても結果が変わらないことから成立する。

111101

111110 or 111110

そこで、1の連続した区間をまとめてしまうと、与えられた操作は

「ある区間の長さを1減らして、それより左のある区間の長さを1増やす」

と言い換えられることがわかる。

 

以上より、文字列を「0で区切られた1が連続する区間の長さの列」と見なして数列に対応させる(これは文字列と1対1に対応する)。

たとえば

10110111→{\(1,2,3\)}

0101→{\( 0,1,1\)}

01100110→{\(0,2,0,2,0\)}

となる。最初の時点で得られる数列をAと置く。

この時操作は「\(i < j\)かつ\(A_j \geqq 1\)となる\((i,j)\)の組を選び\(A_i\)を1増やし\(A_j\)を1減らす」となる。

最終的に得られる数列をBとおくと、Bが満たすべき条件は

  • (Bの総和) = (Aの総和)、すなわち\(\sum_{i=1}^{M}B_i = \sum_{i=1}^{M}A_i\)
  • k=1,2,..M-1に対して(\(B_k\)までの総和) \(\geqq\) (\(A_k\)までの総和)、すなわち\(\sum_{i=1}^{k}B_i \geqq \sum_{i=1}^{k}A_i\)
  • \(\max(B_i-A_i,0)\)の総和が\(K\)以下

となる。(Mは数列の長さ)

最後の条件は操作の回数が\(K\)回以下という条件に対応する。(なお、最適に操作を行えば操作回数は\(|S|\)で抑えられる)

 

このままDPをしても良いが、自分は本番ではさらに次のように条件を言い換えた。

\(C_k = \sum_{i=1}^{k}B_i - \sum_{i=1}^{k}A_i\)とおくと、数列Cが満たすべき条件は

  • \(C_M = 0\)
  • k=1,2,...,M-1に対して\(C_k \geqq 0\)
  • \(\max(C_i-C_{i-1}, 0)\)の総和が\(K\)以下(ここで\(C_0=0\)とする)

となる。3番目の条件に出てくる和を「増分の和」と呼ぶことにする。

 

\(dp[i][j][k] = {i番目の要素まで見てC_i=j、増分の和がkである数列の個数}\)

とおく。

この時

\(dp[i][j][k] = \)

\( (dp[i-1][j-1][k - 1] + dp[i-1][j-2][k-2]+dp[i-1][j-3][k-3]+...)\)

\( + \)

\( (dp[i-1][j][k]+dp[i-1][j+1][k]+dp[i-1][j+2][k]+...+dp[i-1][j+A_i][k])\)

となる。

下の部分が\(j+A_i\)で打ち切られているのは、もともとBのprefixの総和とAのprefixの総和の差を考えていたため、もし差が\(A_i\)より大きく縮むことがあれば、Bに負の要素が出てきてしまうからである。

この遷移は累積和を取ることで一回のiに対し\(O(|S|^2)\)で行うことができ、

全体で\(O(|S|^3)\)になる。

答えは\(\sum_{k=0}^{K}dp[M][0][k]\)である。

提出

https://atcoder.jp/contests/agc046/submissions/14500284

(方針変更によって不必要な配列などが残っています)

感想

累積和とかがいっぱい出てきて混乱しやすいし、実際した。