−目次
AtCoder Beginner Contest 171 C,D,E,F問題メモ
C - One Quadrillion and One Dalmatians
問題
- ある要素列 S は、以下のように定義される
- 各要素は英小文字からなる文字列
- 1~26 番目の要素は、
'a'~'z
' - 27~702 番目の要素は、
'aa','ab',…,'az','ba','bb',…,'zz
' - 以下1文字ずつ増え、同様
- S の N 番目の要素を答えよ
- 1≤N≤1015+1
解法
百一匹わんちゃんならぬ千兆一匹わんちゃん。名も無い時代の集落の 名も無い幼いダルメシアンの 誰も知らないおとぎばなし。
N を26進数に変換すればいいかとパッと思ってしまうが、よく考えるとズレてくる。
'a'~'z'→0~25 と対応させたら、1桁のうちはよいが、その次、27番目の'aa'が示す値は単純に置き換えると00であり、26になってくれない。
一般的な10進数で頭の0は省略するのと同様、この記法では頭の'a'は省略してもよいと考えると、たとえば以下の3つは同じものを数えていることになる。
1~ 26番目の a ~ z 27~ 52番目の aa ~ az 703~728番目の aaa ~ aaz
言うなれば、文字数が増えるたびに0にリセットされている、と捉えることができる。
そのため、まず何文字かを決めるとよい。文字数を固定すると、同じ文字数の中では、26進数の考え方が使える。
累積 1文字の個数 26^1 = 26 26 2文字の個数 26^2 = 676 702 3文字の個数 26^3 = 17576 18278 ...
k 文字の個数の累積和を Dk として、Dk−1<N≤Dk なる k があったら、答えは k 文字である。
よって、k 文字未満の個数を除いて、0始まりとした N−Dk−1−1 を、26進数に変換し、0~25→'a'~'z' と置き換えればよい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import string def solve(n): digit = 1 digit_sum = 0 d = 0 while n > = digit_sum: digit * = 26 digit_sum + = digit d + = 1 n - = (digit_sum - digit) ans = '' for _ in range (d): n, m = divmod (n, 26 ) ans + = string.ascii_lowercase[m] return ans[:: - 1 ] n = int ( input ()) print (solve(n - 1 )) |
D - Replacing
問題
- 長さ N 個の正整数列 A={A1,A2,⋯,AN} がある
- Q 回、以下の操作を行う
- i 回目の操作では、A の、値が Bi である要素を全て Ci に置き換える
- 各操作後について、A の全要素の和を求めよ
- 1≤N,Q≤105
解法
状態管理と差分更新。
現在の A に要素 a が何個あるか、という状態を管理する。これを count[a] とする。
1回のクエリでは、Bi が Ci に置き換わるので、
- 総和は count[Bi]×(Ci−Bi) だけ増える
- count[Bi] は0になる
- count[Ci] は count[Bi] だけ増える
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import sys from collections import Counter, defaultdict n = int (sys.stdin. buffer .readline()) aaa = list ( map ( int , sys.stdin. buffer .readline().split())) q = int (sys.stdin. buffer .readline()) bc = map ( int , sys.stdin. buffer .read().split()) count = defaultdict( int , Counter(aaa)) ans = sum (a * c for a, c in cnt.items()) buf = [] for b, c in zip (bc, bc): if b not in count: buf.append(ans) continue ans + = count[b] * (c - b) count[c] + = count[b] del count[b] buf.append(ans) print ( '\n' .join( map ( str , buf))) |
上記では、count[Ci] に足し込むとき、Ci が存在しないかも知れないので、Counterで要素数を数えた後、defaultdictに変換して使っている。
しかし、わざわざ変換せずとも、Counterそれ自身も同じように存在しないキーでアクセスするとエラーにならず0で初期化してくれるらしい。しらんかった。
E - Red Scarf
問題
- N(偶数)個の数 A={A1,A2,...,AN} があるが、これは明かされない
- ai を、「Ai を除く、他の全ての A のXOR」とする
- a1,a2,...,aN が与えられるので、A1,A2,...,AN としてあり得る値の組を1つ、出力せよ
- 2≤N≤2×105
解法
ai を全てXORしたら、各 Ai はそれぞれ N−1(奇数)回含まれることになる。
偶数回、同じ数をXORしても打ち消されて0に戻るだけなので、結局これは、A1~AN を全て1回ずつXORした数となる。
a1 A2 ⊕ A3 ⊕ A4 各Aiは3回ずつXORされる a2 A1 ⊕ A3 ⊕ A4 a3 A1 ⊕ A2 ⊕ A4 a4 A1 ⊕ A2 ⊕ A3 a1⊕a2⊕a3⊕a4 = A1⊕A2⊕A3⊕A4
これにさらに ai をXORすると、Ai 以外の全ての要素が打ち消され、Ai だけが残る。
a1⊕a2⊕a3⊕a4 ⊕ a1 = A1⊕A2⊕A3⊕A4 ⊕ A2⊕A3⊕A4 = A1
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import sys n, * aaa = map ( int , sys.stdin. buffer .read().split()) all_xor = 0 for a in aaa: all_xor ^ = a buf = [] for a in aaa: buf.append(all_xor ^ a) print ( * buf) |
F - Strivore
問題
- 英小文字からなる文字列 S と、整数 K が与えられる
- S に、ちょうど K 回、以下の操作を繰り返す
- S の好きな位置に、好きな英小文字を挿入する
- 最終的な文字列としてあり得るものの個数を、\mod{10^9+7} で求めよ
- 1 \le K,|S| \le 10^6
解法
1日前にAGC046で数え挙げ問題が多く出題されていたので、そこから着想を得やすかった、という部分はあったかも知れない。
こういう問題は、異なる操作手順であっても、最終的な文字列が同じなら1個としか数えられない。 操作手順に着目するのではなく、最終状態としてあり得るものをどう重複無く数えるか、という点を考えることになる。
最終的な文字列があったとき、挿入手順が複数あっても「考えられる最も左の文字が元からあった文字だとし、他は挿入したもの」と見なして、そのパターンのみ数えると、重複せずに考えられる。
S = oof K = 5 foxfooff o o f ←これを元からあった文字として、これのみ数える o o f ←これを元からあった文字と考えても作れるが、数えない o o f [A] [B] [C] [D]
挿入箇所は、4箇所ある。ここで、[A]には'o'は使うことはできない。「考えられる最も左の文字が元からあった文字」という条件に矛盾する。
同様に、[B]には'o'、[C]には'f'を使うことはできない。これ以外の文字なら何でもいいので、取り得る文字種は共通して25通りとなる。
最後の[D]は、何を入れてもいい。取り得る文字種は26通りとなる。
各 [ ] に何文字入れるかを L_A~L_D とすると、L_A+L_B+L_C+L_D = K となる。
L_A~L_D へのある割り振り方を決めると、その場合の挿入文字のパターン数は、以下のようになる。
o o f [2] [0] [1] [2] 25^2 * 25^0 * 25^1 * 26^2 = 25^(2+0+1) * 26^2
L_A~L_D へ合計 K を割り振る全てのパターンについてこれを計算し、足し合わせたものが答えとなる。
26の指数が a である割り振りパターンについては、挿入文字のパターンは全て 25^{K-a} \times 26^a でまとめて計算できる。
26以外の K-a 個の文字を |S| 箇所に割り振る場合の数は、重複組み合わせで {}_{|S|}H_{K-a} で計算できる。
従って、各 a=0~K につき、以下を計算し、足し合わせればよい。
- {}_{|S|}H_{K-a} \times 25^{K-a} \times 26^a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
def prepare(n, MOD): f = 1 factorials = [ 1 ] for m in range ( 1 , n + 1 ): f * = m f % = MOD factorials.append(f) inv = pow (f, MOD - 2 , MOD) invs = [ 1 ] * (n + 1 ) invs[n] = inv for m in range (n, 1 , - 1 ): inv * = m inv % = MOD invs[m - 1 ] = inv return factorials, invs k = int ( input ()) s = input () l = len (s) MOD = 10 * * 9 + 7 facts, finvs = prepare(k + l, MOD) ans = 0 for i in range (k + 1 ): r = k - i pat = facts[l + r - 1 ] * finvs[l - 1 ] * finvs[r] % MOD ans = (ans + pat * pow ( 25 , r, MOD) * pow ( 26 , i, MOD)) % MOD print (ans) |
AGC046のとある問題も、これと同じように「動かない文字同士のそれぞれの間に、挿入しうる文字が何文字入るか」で状態をまとめて数え上げていたため、 同様のことを試すとたまたま上手くいった、という感じで、AGC046が無かったり、また時間が経ったりすると、着想できたかどうか。
前から貪欲に決定することにする(何らかの性質が出てくる最も左の位置を固定する)と重複がなくなるかも、というのは、解法を試行錯誤する際に浮かぶようにしたい。