AtCoder AGC046-C : Shift 解説
備忘録として
概ね公式解説の通りです 丁寧目に書きました
atcoder.jp
問題概要
に対して、以下の操作を 回まで行う。
- 、
0
、1
であるものを選んで、 を取り除いて の直前に挿入する。
最終的な は何通り考えられるか?
考察
まず、操作は以下のように言い換えられます。
- を
[ 11 ] 0 [ ] 0 [ 1 ] 0 [ 111 ] 0 [ ]
のように0
で区切り、0個以上の 1 からなるいくつかの区間と見る。 すると、操作は「ある区間の1
を1つ選んで、その区間より左の区間に移す」操作とみなせる。
すると、まず以下のことが言えます。
- としても、答えは変わらない。
これは、ある1
を左に移す操作を2回行うならば、1回目の時点で2回目で挿入する箇所に置いておけば良いことから、1
1つあたり1回だけ操作をすれば良いことから言えます。
あとはこれを数えれば良いですが、 注意点として1
1つ1つは区別がありません。
例えば、
0101
->1001
->1100
0101
->1010
->1100
のように、移動方法が違っても同じ文字列ができてしまう可能性があります。
そこで、1
同士の相対的な順序は変えない、というルールをつけて移動する事にします。こうする事で、最終的な文字列に対してどの 1
がどこから来たのかはただ 1 通り定まります。また、これによって生成不能になる文字列が存在しないことはすぐわかります(仮に順序が入れ替わる2つの 1
があるなら、それぞれを最終位置を逆にするような操作もできることから言えます)。
よって、これによって操作と最終的な文字列が 1 対 1 に対応しました。あとは、これに従って dp を行うだけです。
数え上げ
予め、 は上記のようにいくつかの区間 ( は区間の数、 は 番目の区間に含まれる 1
の数)として表現しておきます。
dpテーブルを、以下のように定義します。
- 番目の区間までは操作(その区間への
1
の挿入、その区間からの1
の移動)を終えていて、左の区間への移動を確定させた1
は 個で、「移動することを確定させたが、まだどの区間に挿入するか決めていない」ような1
は 個、となるような、操作方法の数(=文字列の個数)。
番目の区間に対する操作は、以下の2種類です。
ここで、両方の操作をすることは明らかに操作回数の無駄なので考える必要はありません。
また、考察で述べた通り、1
同士の相対順序は決めてあるので、1
を上記の操作で選ぶ際には個数のみ決めれば良いです。
この遷移について、
については、 個以上 個以下選ぶので、 であることを考えると愚直に遷移しても全体で で済みます。
については、愚直に遷移すると毎回 かかってしまうので、工夫します。
というように遷移する必要がある訳ですが、これは の降順に遷移する 事にすれば以下のような遷移としても同じです。
少しわかりにくいので表にすると、以下のような事をしています。 の降順にしないと壊れるのはそういう理由です(公式解説の「1個ずつ分けて移動することにすれば...」の部分?)。
よって、2. の遷移は で出来るようになりました。ここに関しては添字アレルギーでなければ累積和でも良いと思います。僕は面倒くさいのでやりませんでした
以上より、この dp が で計算できました。
提出(C#)
...?
元も子もないですが しても余裕で通るらしいです。ええ...