Before attempting this problem, you should be comfortable with:
Prefix Sum - Computing cumulative sums to determine subarray sums in constant time
Parity (Odd/Even) Properties - Understanding that odd minus even equals odd, and even minus odd equals odd
Dynamic Programming - Breaking down problems into overlapping subproblems with memoization or tabulation
Modular Arithmetic - Applying modulo operations to prevent integer overflow in counting problems
1. Brute Force
Intuition
For each possible subarray, compute its sum and check if it is odd. We try every starting index and extend to every possible ending index, accumulating the sum incrementally.
Algorithm
For each starting index i, initialize a running sum.
Extend the subarray by adding elements one at a time up to index n-1.
After each addition, check if the current sum is odd and increment res if so.
classSolution:defnumOfSubarrays(self, arr: List[int])->int:
n, res =len(arr),0
mod =int(1e9+7)for i inrange(n):
curSum =0for j inrange(i, n):
curSum += arr[j]if curSum %2:
res =(res +1)% mod
return res
publicclassSolution{publicintnumOfSubarrays(int[] arr){int n = arr.length, res =0;int mod =(int)1e9+7;for(int i =0; i < n; i++){int curSum =0;for(int j = i; j < n; j++){
curSum += arr[j];if(curSum %2!=0){
res =(res +1)% mod;}}}return res;}}
classSolution{public:intnumOfSubarrays(vector<int>& arr){int n = arr.size(), res =0;int mod =1e9+7;for(int i =0; i < n; i++){int curSum =0;for(int j = i; j < n; j++){
curSum += arr[j];if(curSum %2!=0){
res =(res +1)% mod;}}}return res;}};
classSolution{/**
* @param {number[]} arr
* @return {number}
*/numOfSubarrays(arr){const n = arr.length;let res =0;const mod =1e9+7;for(let i =0; i < n; i++){let curSum =0;for(let j = i; j < n; j++){
curSum += arr[j];if(curSum %2!==0){
res =(res +1)% mod;}}}return res;}}
publicclassSolution{publicintNumOfSubarrays(int[] arr){int n = arr.Length, res =0;int mod =(int)1e9+7;for(int i =0; i < n; i++){int curSum =0;for(int j = i; j < n; j++){
curSum += arr[j];if(curSum %2!=0){
res =(res +1)% mod;}}}return res;}}
funcnumOfSubarrays(arr []int)int{
n :=len(arr)
res :=0
mod :=int(1e9+7)for i :=0; i < n; i++{
curSum :=0for j := i; j < n; j++{
curSum += arr[j]if curSum%2!=0{
res =(res +1)% mod
}}}return res
}
class Solution {funnumOfSubarrays(arr: IntArray): Int {val n = arr.size
var res =0val mod =1_000_000_007for(i in0 until n){var curSum =0for(j in i until n){
curSum += arr[j]if(curSum %2!=0){
res =(res +1)% mod
}}}return res
}}
classSolution{funcnumOfSubarrays(_ arr:[Int])->Int{let n = arr.count
var res =0let mod =1_000_000_007for i in0..<n {var curSum =0for j in i..<n {
curSum += arr[j]if curSum %2!=0{
res =(res +1)% mod
}}}return res
}}
Time & Space Complexity
Time complexity: O(n2)
Space complexity: O(1)
2. Dynamic Programming (Top-Down)
Intuition
We use memoization to count subarrays ending at each position. For a subarray starting at index i, the parity of its sum depends on the running parity as we extend rightward. By caching results for each (index, parity) state, we avoid redundant calculations.
Algorithm
Define dfs(i, parity) returning the count of odd-sum subarrays starting at index i with the given running parity.
At each step, update the parity by adding the current element modulo 2.
Add 1 to the count if the new parity is odd, then recurse to the next index.
Sum up dfs(i, 0) for all starting indices to get the total.
classSolution:defnumOfSubarrays(self, arr: List[int])->int:
mod =10**9+7
n =len(arr)
memo ={}defdp(i:int, parity:int)->int:if i == n:return0if(i, parity)in memo:return memo[(i, parity)]
new_parity =(parity + arr[i])%2
res = new_parity + dp(i +1, new_parity)
memo[(i, parity)]= res % mod
return memo[(i, parity)]
ans =0for i inrange(n):
ans =(ans + dp(i,0))% mod
return ans
publicclassSolution{int[][] memo;int[] arr;int mod =(int)1e9+7;publicintnumOfSubarrays(int[] arr){int n = arr.length;this.arr = arr;
memo =newint[n][2];for(int i =0; i < n; i++){
memo[i][0]=-1;
memo[i][1]=-1;}int res =0;for(int i =0; i < n; i++){
res =(res +dp(i,0))% mod;}return res;}privateintdp(int i,int parity){if(i == arr.length)return0;if(memo[i][parity]!=-1)return memo[i][parity];int newParity =(parity + arr[i])%2;int res = newParity +dp(i +1, newParity);return memo[i][parity]= res % mod;}}
classSolution{public:int mod =1e9+7;
vector<vector<int>> memo;
vector<int> arr;intnumOfSubarrays(vector<int>& arr){this->arr = arr;int n = arr.size();
memo.assign(n,vector<int>(2,-1));int res =0;for(int i =0; i < n; i++){
res =(res +dp(i,0))% mod;}return res;}intdp(int i,int parity){if(i == arr.size())return0;if(memo[i][parity]!=-1)return memo[i][parity];int newParity =(parity + arr[i])%2;int res = newParity +dp(i +1, newParity);return memo[i][parity]= res % mod;}};
classSolution{/**
* @param {number[]} arr
* @return {number}
*/numOfSubarrays(arr){const mod =1e9+7;const n = arr.length;const memo = Array.from({length: n },()=>Array(2).fill(-1));constdp=(i, parity)=>{if(i === n)return0;if(memo[i][parity]!==-1)return memo[i][parity];const newParity =(parity + arr[i])%2;const res = newParity +dp(i +1, newParity);return(memo[i][parity]= res % mod);};let res =0;for(let i =0; i < n; i++){
res =(res +dp(i,0))% mod;}return res;}}
publicclassSolution{privateint[][] memo;privateint[] arr;privateint mod =(int)1e9+7;publicintNumOfSubarrays(int[] arr){int n = arr.Length;this.arr = arr;
memo =newint[n][];for(int i =0; i < n; i++){
memo[i]=newint[]{-1,-1};}int res =0;for(int i =0; i < n; i++){
res =(res +Dp(i,0))% mod;}return res;}privateintDp(int i,int parity){if(i == arr.Length)return0;if(memo[i][parity]!=-1)return memo[i][parity];int newParity =(parity + arr[i])%2;int res = newParity +Dp(i +1, newParity);return memo[i][parity]= res % mod;}}
funcnumOfSubarrays(arr []int)int{
mod :=int(1e9+7)
n :=len(arr)
memo :=make([][]int, n)for i :=range memo {
memo[i]=[]int{-1,-1}}var dp func(i, parity int)int
dp =func(i, parity int)int{if i == n {return0}if memo[i][parity]!=-1{return memo[i][parity]}
newParity :=(parity + arr[i])%2
res := newParity +dp(i+1, newParity)
memo[i][parity]= res % mod
return memo[i][parity]}
res :=0for i :=0; i < n; i++{
res =(res +dp(i,0))% mod
}return res
}
class Solution {funnumOfSubarrays(arr: IntArray): Int {val mod =1_000_000_007val n = arr.size
val memo =Array(n){IntArray(2){-1}}fundp(i: Int, parity: Int): Int {if(i == n)return0if(memo[i][parity]!=-1)return memo[i][parity]val newParity =(parity + arr[i])%2val res = newParity +dp(i +1, newParity)
memo[i][parity]= res % mod
return memo[i][parity]}var res =0for(i in0 until n){
res =(res +dp(i,0))% mod
}return res
}}
classSolution{funcnumOfSubarrays(_ arr:[Int])->Int{let mod =1_000_000_007let n = arr.count
var memo =[[Int]](repeating:[-1,-1], count: n)funcdp(_ i:Int,_ parity:Int)->Int{if i == n {return0}if memo[i][parity]!=-1{return memo[i][parity]}let newParity =(parity + arr[i])%2let res = newParity +dp(i +1, newParity)
memo[i][parity]= res % mod
return memo[i][parity]}var res =0for i in0..<n {
res =(res +dp(i,0))% mod
}return res
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
3. Dynamic Programming (Bottom-Up)
Intuition
We can convert the top-down approach to bottom-up by processing indices from right to left. For each position, we compute how many odd-sum subarrays can be formed starting there, given either even or odd running parity.
Algorithm
Create a 2D array dp[i][parity] representing counts from index i with given parity.
Iterate from the last index to the first.
For each parity, compute the new parity after including the current element and fill in the dp value.
Sum dp[i][0] for all indices to get the final answer.
classSolution:defnumOfSubarrays(self, arr: List[int])->int:
n =len(arr)
mod =10**9+7
dp =[[0]*2for _ inrange(n +1)]for i inrange(n -1,-1,-1):for parity inrange(2):
new_parity =(parity + arr[i])%2
dp[i][parity]=(new_parity + dp[i +1][new_parity])% mod
res =0for i inrange(n):
res =(res + dp[i][0])% mod
return res
publicclassSolution{publicintnumOfSubarrays(int[] arr){int n = arr.length;int mod =(int)1e9+7;int[][] dp =newint[n +1][2];for(int i = n -1; i >=0; i--){for(int parity =0; parity <=1; parity++){int newParity =(parity + arr[i])%2;
dp[i][parity]=(newParity + dp[i +1][newParity])% mod;}}int res =0;for(int i =0; i < n; i++){
res =(res + dp[i][0])% mod;}return res;}}
classSolution{public:intnumOfSubarrays(vector<int>& arr){int n = arr.size(), mod =1e9+7;
vector<vector<int>>dp(n +1,vector<int>(2,0));for(int i = n -1; i >=0; i--){for(int parity =0; parity <=1; parity++){int newParity =(parity + arr[i])%2;
dp[i][parity]=(newParity + dp[i +1][newParity])% mod;}}int res =0;for(int i =0; i < n; i++){
res =(res + dp[i][0])% mod;}return res;}};
classSolution{/**
* @param {number[]} arr
* @return {number}
*/numOfSubarrays(arr){const n = arr.length;const mod =1e9+7;const dp = Array.from({length: n +1},()=>[0,0]);for(let i = n -1; i >=0; i--){for(let parity =0; parity <=1; parity++){const newParity =(parity + arr[i])%2;
dp[i][parity]=(newParity + dp[i +1][newParity])% mod;}}let res =0;for(let i =0; i < n; i++){
res =(res + dp[i][0])% mod;}return res;}}
publicclassSolution{publicintNumOfSubarrays(int[] arr){int n = arr.Length;int mod =(int)1e9+7;int[][] dp =newint[n +1][];for(int i =0; i <= n; i++){
dp[i]=newint[]{0,0};}for(int i = n -1; i >=0; i--){for(int parity =0; parity <=1; parity++){int newParity =(parity + arr[i])%2;
dp[i][parity]=(newParity + dp[i +1][newParity])% mod;}}int res =0;for(int i =0; i < n; i++){
res =(res + dp[i][0])% mod;}return res;}}
funcnumOfSubarrays(arr []int)int{
n :=len(arr)
mod :=int(1e9+7)
dp :=make([][]int, n+1)for i :=range dp {
dp[i]=[]int{0,0}}for i := n -1; i >=0; i--{for parity :=0; parity <=1; parity++{
newParity :=(parity + arr[i])%2
dp[i][parity]=(newParity + dp[i+1][newParity])% mod
}}
res :=0for i :=0; i < n; i++{
res =(res + dp[i][0])% mod
}return res
}
class Solution {funnumOfSubarrays(arr: IntArray): Int {val n = arr.size
val mod =1_000_000_007val dp =Array(n +1){IntArray(2)}for(i in n -1 downTo 0){for(parity in0..1){val newParity =(parity + arr[i])%2
dp[i][parity]=(newParity + dp[i +1][newParity])% mod
}}var res =0for(i in0 until n){
res =(res + dp[i][0])% mod
}return res
}}
classSolution{funcnumOfSubarrays(_ arr:[Int])->Int{let n = arr.count
let mod =1_000_000_007var dp =[[Int]](repeating:[0,0], count: n +1)for i instride(from: n -1, through:0, by:-1){for parity in0...1{let newParity =(parity + arr[i])%2
dp[i][parity]=(newParity + dp[i +1][newParity])% mod
}}var res =0for i in0..<n {
res =(res + dp[i][0])% mod
}return res
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
4. Prefix Sum - I
Intuition
A subarray has an odd sum when its prefix sum parity differs from the prefix sum at its starting point. If the current prefix sum is odd, pairing it with any previous even prefix sum yields an odd subarray. We track counts of odd and even prefix sums seen so far.
Algorithm
Maintain counters for odd and even prefix sums encountered.
For each element, update the running prefix sum.
If the prefix sum is odd, add 1 (for the subarray from the start) plus the count of previous even prefix sums.
If even, add the count of previous odd prefix sums.
classSolution:defnumOfSubarrays(self, arr: List[int])->int:
cur_sum = odd_cnt = even_cnt = res =0
MOD =10**9+7for n in arr:
cur_sum += n
if cur_sum %2:
res =(res +1+ even_cnt)% MOD
odd_cnt +=1else:
res =(res + odd_cnt)% MOD
even_cnt +=1return res
publicclassSolution{publicintnumOfSubarrays(int[] arr){int curSum =0, oddCnt =0, evenCnt =0, res =0;intMOD=(int)1e9+7;for(int n : arr){
curSum += n;if(curSum %2!=0){
res =(res +1+ evenCnt)%MOD;
oddCnt++;}else{
res =(res + oddCnt)%MOD;
evenCnt++;}}return res;}}
classSolution{public:intnumOfSubarrays(vector<int>& arr){longlong curSum =0, oddCnt =0, evenCnt =0, res =0;constint MOD =1e9+7;for(int n : arr){
curSum += n;if(curSum %2!=0){
res =(res +1+ evenCnt)% MOD;
oddCnt++;}else{
res =(res + oddCnt)% MOD;
evenCnt++;}}return res;}};
classSolution{/**
* @param {number[]} arr
* @return {number}
*/numOfSubarrays(arr){let curSum =0,
oddCnt =0,
evenCnt =0,
res =0;constMOD=1e9+7;for(let n of arr){
curSum += n;if(curSum %2!==0){
res =(res +1+ evenCnt)%MOD;
oddCnt++;}else{
res =(res + oddCnt)%MOD;
evenCnt++;}}return res;}}
publicclassSolution{publicintNumOfSubarrays(int[] arr){int curSum =0, oddCnt =0, evenCnt =0, res =0;int MOD =(int)1e9+7;foreach(int n in arr){
curSum += n;if(curSum %2!=0){
res =(res +1+ evenCnt)% MOD;
oddCnt++;}else{
res =(res + oddCnt)% MOD;
evenCnt++;}}return res;}}
funcnumOfSubarrays(arr []int)int{
curSum, oddCnt, evenCnt, res :=0,0,0,0
mod :=int(1e9+7)for_, n :=range arr {
curSum += n
if curSum%2!=0{
res =(res +1+ evenCnt)% mod
oddCnt++}else{
res =(res + oddCnt)% mod
evenCnt++}}return res
}
class Solution {funnumOfSubarrays(arr: IntArray): Int {var curSum =0var oddCnt =0var evenCnt =0var res =0val mod =1_000_000_007for(n in arr){
curSum += n
if(curSum %2!=0){
res =(res +1+ evenCnt)% mod
oddCnt++}else{
res =(res + oddCnt)% mod
evenCnt++}}return res
}}
classSolution{funcnumOfSubarrays(_ arr:[Int])->Int{var curSum =0, oddCnt =0, evenCnt =0, res =0let mod =1_000_000_007for n in arr {
curSum += n
if curSum %2!=0{
res =(res +1+ evenCnt)% mod
oddCnt +=1}else{
res =(res + oddCnt)% mod
evenCnt +=1}}return res
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
5. Prefix Sum - II
Intuition
We only need to track the parity of the prefix sum (0 for even, 1 for odd). A count array of size 2 stores how many prefix sums of each parity we have seen. For each new element, we look up the count of the opposite parity to find valid subarrays.
Algorithm
Initialize count[0] = 1 to represent the empty prefix (sum 0, which is even).
For each element, toggle the prefix parity by adding the element modulo 2.
Add count[1 - prefix] to the result (subarrays ending here with odd sum).
classSolution:defnumOfSubarrays(self, arr: List[int])->int:
count =[1,0]
prefix = res =0
MOD =10**9+7for num in arr:
prefix =(prefix + num)%2
res =(res + count[1- prefix])% MOD
count[prefix]+=1return res
publicclassSolution{publicintnumOfSubarrays(int[] arr){int[] count ={1,0};int prefix =0, res =0;intMOD=(int)1e9+7;for(int num : arr){
prefix =(prefix + num)%2;
res =(res + count[1- prefix])%MOD;
count[prefix]++;}return res;}}
classSolution{public:intnumOfSubarrays(vector<int>& arr){int count[2]={1,0};int prefix =0, res =0;constint MOD =1e9+7;for(int num : arr){
prefix =(prefix + num)%2;
res =(res + count[1- prefix])% MOD;
count[prefix]++;}return res;}};
classSolution{/**
* @param {number[]} arr
* @return {number}
*/numOfSubarrays(arr){const count =[1,0];let prefix =0,
res =0;constMOD=1e9+7;for(const num of arr){
prefix =(prefix + num)%2;
res =(res + count[1- prefix])%MOD;
count[prefix]++;}return res;}}
publicclassSolution{publicintNumOfSubarrays(int[] arr){int[] count ={1,0};int prefix =0, res =0;int MOD =(int)1e9+7;foreach(int num in arr){
prefix =(prefix + num)%2;
res =(res + count[1- prefix])% MOD;
count[prefix]++;}return res;}}
funcnumOfSubarrays(arr []int)int{
count :=[]int{1,0}
prefix, res :=0,0
mod :=int(1e9+7)for_, num :=range arr {
prefix =(prefix + num)%2
res =(res + count[1-prefix])% mod
count[prefix]++}return res
}
class Solution {funnumOfSubarrays(arr: IntArray): Int {val count =intArrayOf(1,0)var prefix =0var res =0val mod =1_000_000_007for(num in arr){
prefix =(prefix + num)%2
res =(res + count[1- prefix])% mod
count[prefix]++}return res
}}
classSolution{funcnumOfSubarrays(_ arr:[Int])->Int{var count =[1,0]varprefix=0var res =0let mod =1_000_000_007for num in arr {prefix=(prefix+ num)%2
res =(res + count[1-prefix])% mod
count[prefix]+=1}return res
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
Common Pitfalls
Forgetting to Initialize Even Count
When using the prefix sum approach, the empty prefix (sum of zero elements) has an even sum. If you forget to initialize count[0] = 1 or evenCnt = 0 with proper handling, you will miss subarrays that start from index 0.
Confusing Parity Logic
The key insight is that odd - even = odd and even - odd = odd. Some programmers mistakenly check if the current prefix sum is odd and then add the count of odd prefix sums, when they should be adding the count of even prefix sums (since subtracting an even prefix from an odd prefix yields an odd subarray sum).
Not Applying Modulo Correctly
The result can grow very large, so modulo 10^9 + 7 must be applied. A common mistake is applying modulo only at the end instead of during each addition, which can cause integer overflow in languages without arbitrary precision integers.