Pythonで合計が奇数になる部分配列の個数を効率的に求める方法
配列 arr が与えられます。ここで、要素の合計が奇数となる部分配列(サブ配列)の個数を求めます。答えが非常に大きくなる可能性があるため、結果は 109+7 で割った余りとして返します。
例えば、入力が arr = [8,3,7] の場合、出力は 3 になります。すべての部分配列は [8]、[3]、[7]、[8,3]、[3,7]、[8,3,7] の6つであり、それぞれの合計値は 8、3、7、11、10、18 です。このうち奇数となっているのは 3、7、11 の3つであるためです。
解法のアプローチ:累積和の偶奇に着目する
すべての部分配列を素朴に列挙すると計算量が O(n²) 以上になり、大きな入力では非効率です。そこで「累積和(prefix sum)」の偶奇を利用した効率的な手法を使います。
ある部分配列 arr[i..j] の合計が奇数になるのは、「位置 j までの累積和」と「位置 i−1 までの累積和」の偶奇が異なる場合と完全に一致します。したがって、それまでに出現した累積和のうち偶数・奇数それぞれの個数を記録しておけば、各位置で O(1) で答えに加算できます。
アルゴリズムの手順
freqを [1, 0] で初期化します(空の累積和 0 が偶数として最初から1つ存在するとみなすため)。ans(答え)とprefix(累積和)を 0 で初期化します。- 配列
arrの各要素xに対して、次の処理を繰り返します。prefixにxを加算します。ansにfreq[1 ^ (prefix & 1)](現在と逆の偶奇を持つ累積和の個数)を加算します。freq[prefix & 1]を 1 増やします。
- 最後に
ans mod (10^9+7)を返します。
Pythonでの実装例
def solve(arr):
freq = [1, 0]
ans = prefix = 0
for x in arr:
prefix += x
ans += freq[1 ^ (prefix & 1)]
freq[prefix & 1] += 1
return ans % (10**9+7)
arr = [8,3,7]
print(solve(arr))
入力
[8,3,7]
出力
3
計算量
時間計算量は O(n)、空間計算量は O(1)(freq は固定サイズの2要素のみ)です。配列の長さ n が非常に大きい場合でも高速に動作するのが特徴です。
-
Pythonプログラムで数の偶数の約数の合計を求める方法
この記事では、以下の問題文に対する解決策について詳しく解説します。 問題文:ある数が与えられたとき、その数のすべての偶数の約数(因子)の合計を求めて表示します。 アプローチ まず、与えられた数が奇数であるかどうかを確認します。奇数には偶数の約数が存在しないため、その場合は 0 を返します。 数が偶数である場合は、実際の計算に進みます。ここでのポイントは、20(つまり1)以外のすべての項を掛け合わせることで、偶数の約数の合計が得られるという点です。 偶数の約数からすべての奇数を取り除くために、20 に相当する「1」を無視します。この処理を行うことで、残るのは偶数の約数のみとなります。なお、2 は
-
Pythonで数の奇数の約数(奇因子)の合計を求めるプログラム
この記事では、「整数 n が与えられたとき、その数の奇数の約数(奇因子)の合計を求める」という問題の解き方を解説します。 問題文 整数 n が入力として与えられます。求めるのは、n の奇数の約数をすべて足し合わせた値です。 例えば n = 27 の場合、約数は 1, 3, 9, 27 のすべてが奇数であるため、合計は 1 + 3 + 9 + 27 = 40 となります。 アプローチのポイント この問題で最初に行うべきは、偶数の約数をすべて除外することです。 偶数の約数を取り除くには、n が 2 で割り切れなくなるまで繰り返し 2 で割ります。この操作によって n から 2 の因数が完全に