典型90【046 – I Love 46(★3)】をpython解説

Python

AtCoder の問題を解くために必要な実力を付けるために作られた「典型問題」を解いていく企画として、「典型90」があります。

今回は、その内、の46問目である I Love 46(★3)を解説していきたいと思います。
なにか、間違っているところなどありましたら、@EitaSugiまで、ご指導ご鞭撻の程、よろしくお願い致します!

問題名 【046 – I Love 46(★3)】

問題

考察

制約N < 10^5より、配列全てに対して探索すると、O(N^3)の計算量が必要となり、現実的な時間内に計算を完了させることが難しい。そこで、計算量を削減するための工夫が求められる。

では、どのようにリストを圧縮するか?

今回は、(A[i]+B[j]+C[k])%46 == 0となる組み合わせを見つけたいので、各リストの各要素を46で割った余りを考慮すればよいです。
(なぜなら(a + b) mod n = [(a mod n) + (b mod n)] mod nより上記のモジュラ算術の性質を利用します。それぞれの数A[i], B[j], C[k]を46で割った余りを求め、それらの和を46で割った余りが0である組み合わせを見つければ、元の3つの数の和が46の倍数である組み合わせを見つけることができます)

このようにすることで、リスト内の要素がどのような値であっても、その余りは0から45の間の46種類に圧縮されます。

そのため、A,B,Cのリスト内の要素について46で割ったときの余りを求め、それらの値の出現回数をカウントする。この操作により、それぞれの余りがいくつ存在するのかが把握でき、それらを組み合わせる計算を効率的に行うことが可能となります。

その後、全ての可能な余りの組み合わせについて、その和が46の倍数となるかどうかを確認すればよいです。

もし46の倍数となる組み合わせがあれば、その組み合わせの数(該当する余りの出現回数の積)を合計することで、最終的に求めるべき組み合わせの総数を得ることができます。

コード

import collections
#Pythonの標準ライブラリのcollectionsをインポートします。
#これにより、リスト内の要素の出現回数を数えるCounter関数を使用できます。
N = int(input())
A = list(map(int, input().split()))
B = list(map(int, input().split()))
C = list(map(int, input().split()))

# 空のリストnA, nB, nCを作成します。
# これらのリストは、リストA, B, Cの各要素を46で割った余りを格納します。
nA = []
nB = []
nC = []
for i in range(N):
    nA.append(A[i]%46)
    nB.append(B[i]%46)
    nC.append(C[i]%46)

#collections.Counter関数を使用して、リストnA, nB, nCの各要素の出現回数を数えます
count_A = collections.Counter(nA)
count_B = collections.Counter(nB)
count_C = collections.Counter(nC)

cnt = 0
#3つのforループを用いて、0から45までの全ての組み合わせについて、選んだ3つの数(i, j, k)の合計が46の倍数になるかどうかを確認します。
for i in range(46):
    for j in range(46):
        for k in range(46):
            if (i+j+k)%46 == 0:#もし46の倍数になるなら、
                cnt += count_A[i]*count_B[j]*count_C[k]#その組み合わせの数(各リスト内で該当する数の出現回数の積)をカウンタcntに加算します
print(cnt)

まとめ

前の問題【044 – Shift and Swapping(★3)】
次の問題【048 – I will not drop out(★3)】

コメント

タイトルとURLをコピーしました