ABC150 E - Change a Little Bit
問題原文
問題要旨
長さ の、 0と1からなる相異なる数列 , を考える。また、長さ の数列 が与えられる。 に対して以下の操作を繰り返し行い、 にすることを考える。
- のある項を0なら1に、1なら0に変える。この時コストとして、 (その時点で となっているようなものの数)がかかる。
上記に操作を繰り返し行うことで、 [S = T] にするまでにかかる最小のコストを と定める。
考えうる全ての , の組み合わせについて、 を計算し、その総和を で割ったあまりを求めよ。
解法
を 000...000に固定する
まず、 , は相異なると問題文にあるが、 となるようなものはそもそもコストが0なのでまとめて考えてしまって良い。
また結局全パターンについて試されるので、 として、考えうる全ての について答えを求め、 最後に 倍すればいい。
どんな数から に揃えていくか
ここから、 各 についてのみ考える。また、 としているので、 番目が1であることは、その部分が と異なることを表す。
これを踏まえた上で、 「どんな数から に揃えていくか」を考える。
まず、 すでに となっているようなものについては明らかにそのままにしておくのがいい。
またよく考えると、 が小さいようなものから揃えていくのが最適であることがわかる。
各 についてのコストを数える → 各数についてどんな係数がつくのかを考える
次に各 についてのコストを考えていく。
愚直に考えれば、 みたいな式になるが、発想を変えて、『各数についてどんな係数がつくのかを考える』 。
つまり 答えを、 (全ての のパターンについて、 とする時に となっているようなものの数の総和) とする。
また結局右の係数は、 がソート済みであるとするならば、「全ての について、 より右側( 番目含む)に存在する 0 の数」と言い換えられる。
実際の係数のカウント
次は、係数をいかにして数えるかが問題になる。
(6番目が異なる場合) だとして1の右側に何個1が並ぶだろうか?
組み合わせ計算に走りたくなるが、それだと破滅する。ここでも、各数ごとに注目し、「各桁が何回1になりうるか?」を考える。(わかりやすいのでもう桁っていっちゃう)
の部分
ここは1より左側にあるから、コストには影響しない。
の部分
1は固定。また、ある箇所を1に固定する場合、とりうるパターン数は 個。
だが考えやすいので、 の数を , の数を とすると、 個と言える。
の部分
各 について、 1になるパターンと0にパターンは実は半分半分になる。
なので、 の数を とすると、 各 が 1になるパターンは、 個になる。
またそうした が 個あるので、 に現れる1の数は、 個になる。
また、 の部分は自由に決められるので、 全パターンついてでは結局、 個になる。
答えを求める
以上からある項についてのコストの係数がわかり、ある項についてのコストがわかる。(=答えが求められる。)
具体的には、 と、 の和になるが、これを でくくりだし、
が各係数になる。
実装
を前計算しててもいいけど、毎回やっても大丈夫。
N = int(input()) C = [0] + sorted(list(map(int, input().split()))) MOD = 10 ** 9 + 7 ans = 0 for i in range(1, N + 1): l, r = i - 1, N - i ans += C[i] * pow(2, l, MOD) * (pow(2, max(0, r - 1), MOD) * r % MOD + pow(2, r, MOD)) % MOD print(ans * pow(2, N, MOD) % MOD)
感想
むずかしい、、、けど詰められたはず 係数計算部分でコンビネーションに走ったのが失敗でした。。。
各桁ごとに見る を使ったあとでも意識。。。!!!