-1

I am looking at this coding problem:

For a given number n, form a list and insert the following pattern into the list at the same position sequentially:

{ floor(n/2), n%2, floor(n/2) }

...until every element in the list is either 1 or 0. Now, calculate the number of 1s in the range from l to r (1-indexed).

Explanation

Start from n. Then make a list with the following three elements: { floor(n/2), n%2, floor(n/2) }. Now expand -- in-place -- both of those 2 outer elements, considering the new n to be floor(n/2) .

This process will go on until the value of n reduces down to 1 or 0. So it will basically form a tree with 3 branches coming out of every node at every level starting from 1 node (n) at the root.

Input Format

Three integers: n, l, r

Constraints

0 ≤ n < 1012,
0 ≤ r - l ≤ 106,
1 ≤ l ≤ r

Output Format

A single line containing the number of 1s in the given range.

Sample Input

9 6 9

Sample Output

3

I tried to store every number, but run out of memory. How can I calculate the sum of the range without visiting every node?

Community
  • 1
  • 1
danish
  • 9
  • 4

1 Answers1

1

The array can be seen as the in-order sequence of a binary tree, which would also be the tree of recursion if you would apply a naive algorithm.

That binary tree has the following properties:

  • It is perfect

  • Its height h corresponds to the number of binary digits needed to represent n. So in the case of 9 (0b1001), h = 4.

  • It has 2h-1 nodes, which is also the number of elements in the array representation.

  • Nodes at depth i of the tree, all have the same value and correspond to the i th least significant bit of n. Let's call this bit ni with i in [1, h].

  • By consequence the total value of all nodes together is the sum of 2i-1ni for i in [1, h], which is exactly n.

  • A left (or right) subtree of a node at depth i, will correspond to the tree you would get for floor(n/2i).

To find the node corresponding to index j (when 1-based), you would take the binary representation of j using exactly h binary digits (prepadded with "0" if necessary) and then as long as that representation has more than one "1" in it, choose the left branch when the left-most digit is "0", and right otherwise. Then remove that digit and repeat.

We could then determine what the value is of each left subtree that is omitted when a move to the right is made, include the 1 in the node itself, and take the total of those values. This total would represent the number of 1 values at the left of index j. This in turn could be used to know the total of 1 values in any range.

Turned into an algorithm, you would get something like the snippet below. You can run it, enter the values for n, i, j and see the resulting sum:

function sumBetween(n, i, j) {
    if (j < i) return sumBetween(n, j, i); // put range in ascending order
    return sumUntil(n, j) - sumUntil(n, i - 1);
}

function sumUntil(n, i) {
    let numDigits = n.toString(2).length;
    let bitMask = 1 << numDigits;
    if (i >= bitMask) i = bitMask - 1; // Reduce i when exceeding the "array" size
    if (i < 0) i = 0;

    let sum = 0;
    for (bitMask >>= 1; i > 0 && bitMask > 0; bitMask >>= 1) {
        let goRight = i & bitMask; // Extract a bit from i
        let nodeValue = n % 2; // Get value at current node of binary tree
        n >>= 1; // Go down one level in the tree
        if (goRight) sum += nodeValue + n; // Add node value and values in omitted left subtree
        i -= goRight; // Clear bit in i
    }
    return sum;
}

// I/O handling
let inputs = document.querySelectorAll("input");
let result = document.querySelector("span");

document.addEventListener("input", refresh);

function refresh() {
    let [n, i, j] = Array.from(inputs, input => input.value).map(Number);
    let sum = sumBetween(n, i, j);
    result.textContent = sum;
}
refresh();
input { width: 4em }
n: <input type="number" value="9"><br>
i: <input type="number" value="6"><br>
j: <input type="number" value="9"><br>
sum: <span></span>

The algorithm for getting the sum of a range has a time complexity of O(logn), independent of the values for i and j. The space complexity is O(1) -- there's no need to populate an array.

trincot
  • 317,000
  • 35
  • 244
  • 286