Talentd
FleetCode
Make and follow sheets.Sign in

Handling Sum Queries After Update solution and explanation

HardArraySegment Tree8 min read32.4% acceptanceUpdated 9 Apr 2026
Handling Sum Queries After Update || Leetcode-2569 || C++, Java & PythonAryan Mittal · 26:29 · 1K viewsWatch on YouTube
5 more videos on this problem
  1. Handling Sum Queries After Update || Leetcode-2569 || C++, Java & PythonAryan Mittal · 26:29 · 1K views
  2. Leetcode Biweekly Contest 98 | Handling Sum Queries After Update | PythonBayu Aji · 3:19 · 496 views
  3. 【每日一题】LeetCode 2569. Handling Sum Queries After UpdateHuifeng Guan · 29:29 · 445 views
  4. 2569. Handling Sum Queries After Update (Leetcode Hard)Programming Live with Larry · 1:39:19 · 354 views
  5. HANDLING SUM QUERIES AFTER UPDATE | Leetcode 2569 | LAZY PROPOGATION | SEGMENT TREE CONCEPTS |proteinCoder · 10:47 · 72 views
  6. 6358. Handling Sum Queries After Update Leetcode Solution LOGIC JAVA/CPP/ PYTHONCodeStruggle · 0:38 · 62 views

The problem

You are given two 0-indexed arrays nums1 and nums2 and a 2D array queries of queries. There are three types of queries:

  1. For a query of type 1, queries[i] = [1, l, r]. Flip the values from 0 to 1 and from 1 to 0 in nums1 from index l to index r. Both l and r are 0-indexed.
  2. For a query of type 2, queries[i] = [2, p, 0]. For every index 0 <= i < n, set nums2[i] = nums2[i] + nums1[i] * p.
  3. For a query of type 3, queries[i] = [3, 0, 0]. Find the sum of the elements in nums2.

Return an array containing all the answers to the third type queries.

Example 1:

Input: nums1 = [1,0,1], nums2 = [0,0,0], queries = [[1,1,1],[2,1,0],[3,0,0]]
Output: [3]
Explanation: After the first query nums1 becomes [1,1,1]. After the second query, nums2 becomes [1,1,1], so the answer to the third query is 3. Thus, [3] is returned.

Example 2:

Input: nums1 = [1], nums2 = [5], queries = [[2,0,0],[3,0,0]]
Output: [5]
Explanation: After the first query, nums2 remains [5], so the answer to the second query is 5. Thus, [5] is returned.

Constraints:

  • 1 <= nums1.length,nums2.length <= 105
  • nums1.length = nums2.length
  • 1 <= queries.length <= 105
  • queries[i].length = 3
  • 0 <= l <= r <= nums1.length - 1
  • 0 <= p <= 106
  • 0 <= nums1[i] <= 1
  • 0 <= nums2[i] <= 109

Approach overview

Problem Overview: You maintain two arrays. Queries flip bits in nums1, add a multiple of the number of ones in nums1 to nums2, or ask for the total sum of nums2. The challenge is handling frequent range updates and keeping the count of ones available instantly for later queries.

Approach 1: Naive Iterative Approach (O(n) per update, O(q·n) overall)

The straightforward method directly applies each query. For a flip operation, iterate through the specified range in nums1 and toggle each bit. For the second query type, count the number of ones in nums1 by iterating through the array, then add p * ones to a running total of nums2. This approach relies purely on array traversal using basic operations on an array. It works for small inputs but becomes too slow when both the array size and number of queries grow large.

Approach 2: Segment Tree Optimization for Range Updates (O((n + q) log n) time, O(n) space)

The optimized approach stores the count of ones in nums1 using a segment tree. Each node tracks how many ones exist in its range. When a query flips a range, instead of updating each element individually, the tree performs a lazy range flip: the stored count becomes segment_length - current_ones. Lazy propagation postpones updates to children until necessary, keeping operations efficient.

For the second query type, the algorithm reads the total number of ones directly from the root of the tree in O(1). Multiply that value by p and add it to a running sum representing the total of nums2. Since the problem only asks for the sum of nums2, there is no need to update each element individually. The third query simply outputs this stored sum. This design converts expensive full-array operations into logarithmic updates and constant-time queries.

Recommended for interviews: Interviewers expect the Segment Tree with lazy propagation solution. The brute-force iteration shows you understand the mechanics of the queries, but it does not scale. Using a segment tree to maintain the number of ones while supporting fast range flips demonstrates strong knowledge of range query data structures.

Approach 1: Naive Iterative Approach

This approach involves diving directly into the queries and processing them straightforwardly. We iterate through the queries and process each based on their type. This is simple to implement but not optimized for large input sizes, particularly for type 1 queries which require range flips.

def handleQueriesNaive(nums1, nums2, queries):
    results = []
    for query in queries:
        type = query[0]
        l = query[1]
        r = query[2]
        if type == 1:
            for i in range(l, r + 1):
                nums1[i] = 1 - nums1[i]
        elif type == 2:
            for i in range(len(nums2)):
                nums2[i] += nums1[i] * l
        elif type == 3:
            results.append(sum(nums2))
    return results
import java.util.*;

public class Solution {
    public List<Integer> handleQueriesNaive(int[] nums1, int[] nums2, int[][] queries) {
        List<Integer> results = new ArrayList<>();
        for (int[] query : queries) {
            int type = query[0], l = query[1], r = query[2];
            if (type == 1) {
                for (int i = l; i <= r; ++i) {
                    nums1[i] = 1 - nums1[i];
                }
            } else if (type == 2) {
                for (int i = 0; i < nums2.length; ++i) {
                    nums2[i] += nums1[i] * l;
                }
            } else if (type == 3) {
                int sum = 0;
                for (int num : nums2) sum += num;
                results.add(sum);
            }
        }
        return results;
    }
}
#include <iostream>
#include <vector>

using namespace std;

void flip(vector<int>& nums1, int l, int r) {
    for (int i = l; i <= r; i++) {
        nums1[i] = 1 - nums1[i];
    }
}

vector<int> handleQueriesNaive(vector<int>& nums1, vector<int>& nums2, vector<vector<int>>& queries) {
    vector<int> results;
    for (auto& query : queries) {
        int type = query[0], l = query[1], r = query[2];
        if (type == 1) {
            flip(nums1, l, r);
        } else if (type == 2) {
            for (int i = 0; i < nums2.size(); ++i) {
                nums2[i] += nums1[i] * l;
            }
        } else if (type == 3) {
            results.push_back(accumulate(nums2.begin(), nums2.end(), 0));
        }
    }
    return results;
}
function handleQueriesNaive(nums1, nums2, queries) {
    const results = [];
    for (const query of queries) {
        const [type, l, r] = query;
        if (type === 1) {
            for (let i = l; i <= r; i++) {
                nums1[i] = 1 - nums1[i];
            }
        } else if (type === 2) {
            for (let i = 0; i < nums2.length; i++) {
                nums2[i] += nums1[i] * l;
            }
        } else if (type === 3) {
            results.push(nums2.reduce((a, b) => a + b, 0));
        }
    }
    return results;
}
#include <stdio.h>
#include <stdlib.h>

void flip(int* nums1, int l, int r) {
    for (int i = l; i <= r; ++i) {
        nums1[i] = 1 - nums1[i];
    }
}

int* handleQueriesNaive(int* nums1, int nums1Size, int* nums2, int nums2Size, int** queries, int queriesSize, int* queriesColSize, int* returnSize) {
    int* results = malloc(queriesSize * sizeof(int));
    *returnSize = 0;
    for (int i = 0; i < queriesSize; ++i) {
        int type = queries[i][0];
        int l = queries[i][1];
        int r = queries[i][2];
        if (type == 1) {
            flip(nums1, l, r);
        } else if (type == 2) {
            for (int j = 0; j < nums2Size; j++) {
                nums2[j] += nums1[j] * l;
            }
        } else if (type == 3) {
            int sum = 0;
            for (int j = 0; j < nums2Size; j++) {
                sum += nums2[j];
            }
            results[(*returnSize)++] = sum;
        }
    }
    return results;
}
using System;
using System.Collections.Generic;

public class Solution {
    public IList<int> HandleQueriesNaive(int[] nums1, int[] nums2, int[][] queries) {
        List<int> results = new List<int>();
        foreach (var query in queries) {
            int type = query[0], l = query[1], r = query[2];
            if (type == 1) {
                for (int i = l; i <= r; ++i) {
                    nums1[i] = 1 - nums1[i];
                }
            } else if (type == 2) {
                for (int i = 0; i < nums2.Length; ++i) {
                    nums2[i] += nums1[i] * l;
                }
            } else if (type == 3) {
                int sum = 0;
                foreach (var num in nums2) sum += num;
                results.Add(sum);
            }
        }
        return results;
    }
}

Python's simplicity allows for a concise implementation following the same logic as the previous languages. We leverage list operations and comprehensions for clarity and efficiency.

Complexity

Time Complexity: O(n * m) overall, due to potential repeated iteration for range modification.
Space Complexity: O(1) for in-place modifications, with O(q) for output storage.

Try this approach in the editor

Approach 2: Segment Tree Optimization for Range Updates

Using a Segment Tree allows us to efficiently handle range updates, particularly suitable for type 1 queries that require flipping elements over a range. This optimization ensures that updates and queries are both handled in logarithmic time.

#include <iostream>
#include <vector>

using namespace std;

class SegmentTree {
    vector<int> tree, lazy;
    int n;

    void build(const vector<int>& nums1, int node, 0, int end) {
        if (start == end) {
            tree[node] = nums1[start];
        } else {
            int mid = (start + end) / 2;
            build(nums1,2 * node + 1, start, mid);
            build(nums1,2 * node + 2, mid + 1, end);
            tree[node] = tree[2 * node + 1] + tree[2 * node + 2];
        }
    }

    void updateRangeUtil(int node, int start, int end, int l, int r, int val) {
        if (lazy[node] != 0) {
            tree[node] = (end - start + 1) - tree[node];
            if (start != end) {
                lazy[node * 2 + 1] ^= 1;
                lazy[node * 2 + 2] ^= 1;
            }
            lazy[node] = 0;
        }

        if (start > end || start > r || end < l)
            return;

        if (start >= l && end <= r) {
            tree[node] = (end - start + 1) - tree[node];
            if (start != end) {
                lazy[node * 2 + 1] ^= 1;
                lazy[node * 2 + 2] ^= 1;
            }
            return;
        }

        int mid = (start + end) / 2;
        updateRangeUtil(node * 2 + 1, start, mid, l, r, val);
        updateRangeUtil(node * 2 + 2, mid + 1, end, l, r, val);
        tree[node] = tree[node * 2 + 1] + tree[node * 2 + 2];
    }

public:
    SegmentTree(const vector<int>& nums1) : n(nums1.size()) {
        tree.resize(4 * n);
        lazy.resize(4 * n);
        build(nums1, 0, 0, n - 1);
    }

    void updateRange(int l, int r, int val) {
        updateRangeUtil(0, 0, n - 1, l, r, val);
    }

    int querySum() {
        return tree[0];
    }
};

vector<int> handleQueriesSegmentTree(vector<int>& nums1, vector<int>& nums2, vector<vector<int>>& queries) {
    int n = nums1.size();
    SegmentTree st(nums1);
    vector<int> results;

    for (auto& query : queries) {
        int type = query[0], l = query[1], r = query[2];
        if (type == 1) {
            st.updateRange(l, r, 1);
        } else if (type == 2) {
            int totalOnes = st.querySum();
            for (int i = 0; i < n; ++i) {
                nums2[i] += (nums1[i] * totalOnes);
            }
        } else if (type == 3) {
            results.push_back(accumulate(nums2.begin(), nums2.end(), 0));
        }
    }
    return results;
}

This solution uses a Segment Tree to manage range updates efficiently. The segment tree supports lazy propagation, which allows us to update ranges of nums1 in logarithmic time. We utilize the tree to keep track of the number of ones in any segment, which is used when applying type 2 queries.

Complexity

Time Complexity: O(log n) per update and query on the segment tree, much improved for large ranges.
Space Complexity: O(n) for the segment tree structure.

Try this approach in the editor

Approach 3: Segment Tree

According to the problem description:

  • Operation 1 is to reverse all numbers in the index range [l,..r] of array nums1, that is, change 0 to 1 and 1 to 0.
  • Operation 3 is to sum all numbers in array nums2.
  • Operation 2 is to add the sum of all numbers in array nums2 with p times the sum of all numbers in array nums1, that is, sum(nums2) = sum(nums2) + p * sum(nums1).

Therefore, we actually only need to maintain the segment sum of array nums1, which can be implemented through a segment tree.

We define each node of the segment tree as Node, each node contains the following attributes:

  • l: The left endpoint of the node, the index starts from 1.
  • r: The right endpoint of the node, the index starts from 1.
  • s: The segment sum of the node.
  • lazy: The lazy tag of the node.

The segment tree mainly has the following operations:

  • build(u, l, r): Build the segment tree.
  • pushdown(u): Propagate the lazy tag.
  • pushup(u): Update the information of the parent node with the information of the child nodes.
  • modify(u, l, r): Modify the segment sum. In this problem, it is to reverse each number in the segment, so the segment sum s = r - l + 1 - s.
  • query(u, l, r): Query the segment sum.

First, calculate the sum of all numbers in array nums2, denoted as s.

When executing operation 1, we only need to call modify(1, l + 1, r + 1).

When executing operation 2, we update s = s + p times query(1, 1, n).

When executing operation 3, we just need to add s to the answer array.

The time complexity is O(n + m times log n), and the space complexity is O(n). Where n and m are the lengths of arrays nums1 and queries respectively.

type node struct {
	l, r, s, lazy int
}

type segmentTree struct {
	nums []int
	tr   []*node
}

func newSegmentTree(nums []int) *segmentTree {
	n := len(nums)
	tr := make([]*node, n<<2)
	for i := range tr {
		tr[i] = &node{}
	}
	t := &segmentTree{nums, tr}
	t.build(1, 1, n)
	return t
}

func (t *segmentTree) build(u, l, r int) {
	t.tr[u].l, t.tr[u].r = l, r
	if l == r {
		t.tr[u].s = t.nums[l-1]
		return
	}
	mid := (l + r) >> 1
	t.build(u<<1, l, mid)
	t.build(u<<1|1, mid+1, r)
	t.pushup(u)
}

func (t *segmentTree) modify(u, l, r int) {
	if t.tr[u].l >= l && t.tr[u].r <= r {
		t.tr[u].lazy ^= 1
		t.tr[u].s = t.tr[u].r - t.tr[u].l + 1 - t.tr[u].s
		return
	}
	t.pushdown(u)
	mid := (t.tr[u].l + t.tr[u].r) >> 1
	if l <= mid {
		t.modify(u<<1, l, r)
	}
	if r > mid {
		t.modify(u<<1|1, l, r)
	}
	t.pushup(u)
}

func (t *segmentTree) query(u, l, r int) int {
	if t.tr[u].l >= l && t.tr[u].r <= r {
		return t.tr[u].s
	}
	t.pushdown(u)
	mid := (t.tr[u].l + t.tr[u].r) >> 1
	res := 0
	if l <= mid {
		res += t.query(u<<1, l, r)
	}
	if r > mid {
		res += t.query(u<<1|1, l, r)
	}
	return res
}

func (t *segmentTree) pushup(u int) {
	t.tr[u].s = t.tr[u<<1].s + t.tr[u<<1|1].s
}

func (t *segmentTree) pushdown(u int) {
	if t.tr[u].lazy == 1 {
		mid := (t.tr[u].l + t.tr[u].r) >> 1
		t.tr[u<<1].s = mid - t.tr[u].l + 1 - t.tr[u<<1].s
		t.tr[u<<1].lazy ^= 1
		t.tr[u<<1|1].s = t.tr[u].r - mid - t.tr[u<<1|1].s
		t.tr[u<<1|1].lazy ^= 1
		t.tr[u].lazy ^= 1
	}
}

func handleQuery(nums1 []int, nums2 []int, queries [][]int) (ans []int64) {
	tree := newSegmentTree(nums1)
	var s int64
	for _, x := range nums2 {
		s += int64(x)
	}
	for _, q := range queries {
		if q[0] == 1 {
			tree.modify(1, q[1]+1, q[2]+1)
		} else if q[0] == 2 {
			s += int64(q[1] * tree.query(1, 1, len(nums1)))
		} else {
			ans = append(ans, s)
		}
	}
	return
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Naive Iterative Approach

Time Complexity: O(n * m) overall, due to potential repeated iteration for range modification.
Space Complexity: O(1) for in-place modifications, with O(q) for output storage.

Segment Tree Optimization for Range Updates

Time Complexity: O(log n) per update and query on the segment tree, much improved for large ranges.
Space Complexity: O(n) for the segment tree structure.

Segment Tree–

Complexity analysis

ApproachTimeSpaceWhen to Use
Naive Iterative ApproachO(q · n)O(1)Small arrays or when implementing a quick baseline solution
Segment Tree with Lazy PropagationO((n + q) log n)O(n)Large input sizes with many range flips and queries

Frequently asked questions

Is Handling Sum Queries After Update easy or hard?

Handling Sum Queries After Update is classified as a Hard problem on LeetCode. The difficulty comes from combining range updates, lazy propagation, and query optimization while avoiding repeated full-array operations.

Handling Sum Queries After Update Python/Java solution

Implement the optimized solution by building a segment tree that stores the count of ones for each range. Range flip operations use lazy propagation, and a variable tracks the running sum of nums2. The same logic translates cleanly to Python, Java, and C++ implementations.

What is the best approach for Handling Sum Queries After Update?

The most efficient solution uses a Segment Tree with lazy propagation to track the number of ones in nums1. Range flip operations update the tree in O(log n), and the total count of ones is always available at the root. This allows type‑2 queries to update the sum in constant time.

How to solve Handling Sum Queries After Update in O((n+q) log n)?

Build a segment tree over nums1 that stores the count of ones in each segment. Use lazy propagation to flip bits in a range efficiently by converting the stored count to segment_length minus current_ones. Maintain a running sum of nums2 and update it using the ones count whenever a type‑2 query appears.

Is Handling Sum Queries After Update asked at Google/Amazon/Meta?

Problems involving segment trees and lazy propagation frequently appear in interviews at companies like Google, Amazon, and Meta. This specific pattern—range updates combined with aggregate queries—is a classic advanced data structure interview topic.

What data structure is used in Handling Sum Queries After Update?

The key data structure is a Segment Tree with lazy propagation. It efficiently handles range bit flips while maintaining the count of ones needed to compute updates to the total sum of nums2.

What is the time complexity of Handling Sum Queries After Update?

The optimal approach runs in O((n + q) log n) time using a segment tree. Each range flip update takes O(log n), while reading the total number of ones and processing sum queries is O(1). Space complexity is O(n) for the tree structure.

Comments

Now write it yourselfClose this page and solve Handling Sum Queries After Update in the editor, without looking.
Practise Handling Sum Queries After Update