Facebook Pixel

2426. Number of Pairs Satisfying Inequality

Problem Description

You are given two integer arrays nums1 and nums2, both of size n, and an integer diff. Your task is to find the number of pairs (i, j) that satisfy both of these conditions:

  1. 0 <= i < j <= n - 1 (meaning i comes before j in the arrays)
  2. nums1[i] - nums1[j] <= nums2[i] - nums2[j] + diff

The second condition can be understood as: the difference between elements at positions i and j in nums1 should not exceed the difference between elements at the same positions in nums2 plus the given diff value.

For example, if you have:

  • nums1 = [3, 5]
  • nums2 = [1, 2]
  • diff = 1

You would check the pair (0, 1):

  • Left side: nums1[0] - nums1[1] = 3 - 5 = -2
  • Right side: nums2[0] - nums2[1] + diff = 1 - 2 + 1 = 0
  • Since -2 <= 0, this pair satisfies the condition

The problem asks you to count all such valid pairs and return the total count.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is to rearrange the given inequality to make it easier to work with. Starting with nums1[i] - nums1[j] <= nums2[i] - nums2[j] + diff, we can rearrange the terms by grouping elements from the same index together:

nums1[i] - nums2[i] <= nums1[j] - nums2[j] + diff

This transformation is crucial because now we can define a new array where each element is nums[k] = nums1[k] - nums2[k]. The problem then simplifies to finding pairs (i, j) where i < j and nums[i] <= nums[j] + diff.

Now we need to count how many valid pairs exist. For each position j, we want to count how many positions i before it (where i < j) satisfy nums[i] <= nums[j] + diff. This is essentially asking: "For each element, how many elements before it are at most nums[j] + diff?"

This type of query - counting elements less than or equal to a certain value in a dynamic set - is a classic use case for data structures like Binary Indexed Tree (Fenwick Tree) or Segment Tree. As we iterate through the array from left to right:

  1. For each position j, we query how many elements we've seen so far are <= nums[j] + diff
  2. Then we add nums[j] to our data structure for future queries

The Binary Indexed Tree is particularly efficient here because it supports both point updates and range queries in O(log n) time. Since the values might be negative or very large, we apply an offset (like 40000) to ensure all values are positive and within a reasonable range for the tree implementation.

Learn more about Segment Tree, Binary Search, Divide and Conquer and Merge Sort patterns.

Solution Approach

The solution implements the intuition using a Binary Indexed Tree (Fenwick Tree) to efficiently count valid pairs.

Step 1: Transform the Problem

First, we create a transformed view of the problem. For each index, we calculate v = nums1[i] - nums2[i]. This transforms our inequality into finding pairs where v[i] <= v[j] + diff with i < j.

Step 2: Binary Indexed Tree Setup

The BinaryIndexedTree class is initialized with a size of 10^5 to handle the range of possible values. The tree supports:

  • update(x, delta): Adds delta to the count at position x
  • query(x): Returns the sum of all elements from index 1 to x
  • lowbit(x): Helper function that returns x & -x, which isolates the rightmost set bit

Step 3: Process Elements and Count Pairs

The main algorithm iterates through both arrays simultaneously:

for a, b in zip(nums1, nums2):
    v = a - b  # Calculate the difference
    ans += tree.query(v + diff + 40000)  # Count valid pairs
    tree.update(v + 40000, 1)  # Add current element to tree

For each position j:

  1. Calculate v = nums1[j] - nums2[j]
  2. Query the tree for count of all elements <= v + diff. The offset of 40000 is added to handle negative values
  3. Add this count to our answer (these are all valid pairs with current position as j)
  4. Update the tree by adding the current v value (with offset) for future queries

Why the Offset of 40000?

Since the Binary Indexed Tree uses 1-based indexing and needs positive indices, we add 40000 to shift all possible values into a positive range. This ensures that even if v is negative (as low as around -40000), the index v + 40000 remains positive.

Time Complexity: O(n log n) where n is the length of the arrays. Each query and update operation takes O(log n) time, and we perform n such operations.

Space Complexity: O(10^5) for the Binary Indexed Tree array, which is constant space relative to the input size.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to understand how the solution works:

Given:

  • nums1 = [3, 2, 5]
  • nums2 = [2, 2, 1]
  • diff = 1

Step 1: Transform the arrays

First, we calculate v[i] = nums1[i] - nums2[i] for each index:

  • v[0] = 3 - 2 = 1
  • v[1] = 2 - 2 = 0
  • v[2] = 5 - 1 = 4

So our transformed array is v = [1, 0, 4].

Step 2: Process each element with Binary Indexed Tree

We'll use a Binary Indexed Tree (BIT) to count valid pairs. For simplicity in this example, let's use a smaller offset of 10 instead of 40000.

Iteration 1 (i=0, v=1):

  • Query: How many elements in BIT are ≤ v + diff + offset = 1 + 1 + 10 = 12?
    • BIT is empty, so count = 0
  • Update: Add v + offset = 1 + 10 = 11 to BIT
  • Running answer: 0

Iteration 2 (i=1, v=0):

  • Query: How many elements in BIT are ≤ v + diff + offset = 0 + 1 + 10 = 11?
    • BIT contains one element at index 11, so count = 1
    • This represents the valid pair (0, 1) where v[0]=1 ≤ v[1]+diff=0+1=1
  • Update: Add v + offset = 0 + 10 = 10 to BIT
  • Running answer: 0 + 1 = 1

Iteration 3 (i=2, v=4):

  • Query: How many elements in BIT are ≤ v + diff + offset = 4 + 1 + 10 = 15?
    • BIT contains elements at indices 10 and 11, so count = 2
    • This represents valid pairs:
      • (0, 2): v[0]=1 ≤ v[2]+diff=4+1=5
      • (1, 2): v[1]=0 ≤ v[2]+diff=4+1=5
  • Update: Add v + offset = 4 + 10 = 14 to BIT
  • Running answer: 1 + 2 = 3

Result: The total number of valid pairs is 3.

Verification: Let's verify our three pairs using the original condition:

  1. Pair (0,1): nums1[0]-nums1[1] = 3-2 = 1nums2[0]-nums2[1]+diff = 2-2+1 = 1
  2. Pair (0,2): nums1[0]-nums1[2] = 3-5 = -2nums2[0]-nums2[2]+diff = 2-1+1 = 2
  3. Pair (1,2): nums1[1]-nums1[2] = 2-5 = -3nums2[1]-nums2[2]+diff = 2-1+1 = 2

All three pairs satisfy the condition, confirming our answer of 3.

Solution Implementation

1from typing import List
2
3
4class BinaryIndexedTree:
5    """Binary Indexed Tree (Fenwick Tree) for efficient prefix sum queries and updates."""
6  
7    def __init__(self, n: int) -> None:
8        """
9        Initialize the Binary Indexed Tree.
10      
11        Args:
12            n: The size of the tree (1-indexed, so actual array size is n+1)
13        """
14        self.n = n
15        self.c = [0] * (n + 1)  # Tree array, 1-indexed
16
17    @staticmethod
18    def lowbit(x: int) -> int:
19        """
20        Get the lowest set bit of x.
21      
22        Args:
23            x: Input integer
24          
25        Returns:
26            The value of the lowest set bit
27        """
28        return x & -x
29
30    def update(self, x: int, delta: int) -> None:
31        """
32        Update the value at index x by adding delta.
33      
34        Args:
35            x: The index to update (1-indexed)
36            delta: The value to add
37        """
38        while x <= self.n:
39            self.c[x] += delta
40            x += BinaryIndexedTree.lowbit(x)
41
42    def query(self, x: int) -> int:
43        """
44        Query the prefix sum from index 1 to x.
45      
46        Args:
47            x: The end index of the range (1-indexed)
48          
49        Returns:
50            The sum of elements from index 1 to x
51        """
52        sum_value = 0
53        while x > 0:
54            sum_value += self.c[x]
55            x -= BinaryIndexedTree.lowbit(x)
56        return sum_value
57
58
59class Solution:
60    def numberOfPairs(self, nums1: List[int], nums2: List[int], diff: int) -> int:
61        """
62        Count the number of pairs (i, j) where i < j and 
63        nums1[i] - nums1[j] <= nums2[i] - nums2[j] + diff.
64      
65        This can be rewritten as: (nums1[i] - nums2[i]) <= (nums1[j] - nums2[j]) + diff
66      
67        Args:
68            nums1: First array of integers
69            nums2: Second array of integers
70            diff: The difference threshold
71          
72        Returns:
73            The count of valid pairs
74        """
75        # Create a BIT with size 10^5 to handle the range of possible values
76        tree = BinaryIndexedTree(100000)
77      
78        # Initialize answer counter
79        answer = 0
80      
81        # Process each pair of elements from nums1 and nums2
82        for a, b in zip(nums1, nums2):
83            # Calculate the difference value for current position
84            value = a - b
85          
86            # Query how many previous values are <= value + diff
87            # Add 40000 as offset to handle negative values (map to positive range)
88            answer += tree.query(value + diff + 40000)
89          
90            # Update the tree with current value (also with offset)
91            tree.update(value + 40000, 1)
92      
93        return answer
94
1/**
2 * Binary Indexed Tree (Fenwick Tree) implementation for efficient range sum queries
3 * and point updates in O(log n) time complexity
4 */
5class BinaryIndexedTree {
6    private int n;        // Size of the tree
7    private int[] c;      // Tree array (1-indexed)
8
9    /**
10     * Constructor to initialize the Binary Indexed Tree
11     * @param n Size of the tree
12     */
13    public BinaryIndexedTree(int n) {
14        this.n = n;
15        this.c = new int[n + 1];  // 1-indexed array
16    }
17
18    /**
19     * Calculate the lowest set bit of x
20     * This determines the range of responsibility for each node
21     * @param x Input number
22     * @return The value of the lowest set bit
23     */
24    public static final int lowbit(int x) {
25        return x & -x;
26    }
27
28    /**
29     * Update the value at position x by adding delta
30     * Propagates the change to all affected nodes in the tree
31     * @param x Position to update (1-indexed)
32     * @param delta Value to add
33     */
34    public void update(int x, int delta) {
35        while (x <= n) {
36            c[x] += delta;
37            x += lowbit(x);  // Move to next responsible node
38        }
39    }
40
41    /**
42     * Query the prefix sum from index 1 to x
43     * @param x Upper bound of the range (1-indexed)
44     * @return Sum of elements from index 1 to x
45     */
46    public int query(int x) {
47        int sum = 0;
48        while (x > 0) {
49            sum += c[x];
50            x -= lowbit(x);  // Move to previous range
51        }
52        return sum;
53    }
54}
55
56/**
57 * Solution class for counting pairs that satisfy the given condition
58 */
59class Solution {
60    /**
61     * Count the number of pairs (i, j) where i < j and 
62     * nums1[i] - nums1[j] <= nums2[i] - nums2[j] + diff
63     * 
64     * Rearranging: nums1[i] - nums2[i] <= nums1[j] - nums2[j] + diff
65     * 
66     * @param nums1 First array
67     * @param nums2 Second array
68     * @param diff The difference threshold
69     * @return Number of valid pairs
70     */
71    public long numberOfPairs(int[] nums1, int[] nums2, int diff) {
72        // Initialize BIT with size 100000 to handle the range of possible values
73        BinaryIndexedTree tree = new BinaryIndexedTree(100000);
74        long answer = 0;
75      
76        // Process each element
77        for (int i = 0; i < nums1.length; ++i) {
78            // Calculate the difference for current position
79            int value = nums1[i] - nums2[i];
80          
81            // Count how many previous elements satisfy the condition
82            // Add 40000 offset to handle negative values (shift to positive range)
83            answer += tree.query(value + diff + 40000);
84          
85            // Add current value to the tree for future queries
86            tree.update(value + 40000, 1);
87        }
88      
89        return answer;
90    }
91}
92
1class BinaryIndexedTree {
2public:
3    int size;                    // Size of the tree (number of elements)
4    vector<int> tree;            // The BIT array (1-indexed)
5
6    // Constructor: Initialize BIT with given size
7    BinaryIndexedTree(int _size)
8        : size(_size)
9        , tree(_size + 1) {}
10
11    // Update operation: Add delta to position x
12    // Time complexity: O(log n)
13    void update(int x, int delta) {
14        // Traverse all ancestors of node x
15        while (x <= size) {
16            tree[x] += delta;
17            x += lowbit(x);      // Move to next node that needs updating
18        }
19    }
20
21    // Query operation: Get prefix sum from index 1 to x
22    // Time complexity: O(log n)
23    int query(int x) {
24        int sum = 0;
25        // Traverse all nodes that contribute to the sum
26        while (x > 0) {
27            sum += tree[x];
28            x -= lowbit(x);      // Move to parent node
29        }
30        return sum;
31    }
32
33    // Helper function: Get the lowest set bit of x
34    // This determines the range of responsibility for each node
35    int lowbit(int x) {
36        return x & -x;
37    }
38};
39
40class Solution {
41public:
42    // Count pairs (i, j) where i < j and nums1[i] - nums1[j] <= nums2[i] - nums2[j] + diff
43    // Rearranging: nums1[i] - nums2[i] <= nums1[j] - nums2[j] + diff
44    long long numberOfPairs(vector<int>& nums1, vector<int>& nums2, int diff) {
45        // Create BIT with size 100000 to handle range of possible values
46        BinaryIndexedTree* tree = new BinaryIndexedTree(1e5);
47      
48        long long pairCount = 0;
49      
50        // Process each element
51        for (int i = 0; i < nums1.size(); ++i) {
52            // Calculate difference for current position
53            int currentDiff = nums1[i] - nums2[i];
54          
55            // Count how many previous elements satisfy the condition
56            // We need to count elements with value <= currentDiff + diff
57            // Add 40000 offset to handle negative values (shift to positive range)
58            pairCount += tree->query(currentDiff + diff + 40000);
59          
60            // Add current difference to the tree for future queries
61            tree->update(currentDiff + 40000, 1);
62        }
63      
64        return pairCount;
65    }
66};
67
1// Binary Indexed Tree (Fenwick Tree) for efficient prefix sum queries and updates
2let size: number;                    // Size of the tree (number of elements)
3let tree: number[];                  // The BIT array (1-indexed)
4
5// Initialize BIT with given size
6function initializeBIT(_size: number): void {
7    size = _size;
8    tree = new Array(_size + 1).fill(0);
9}
10
11// Helper function: Get the lowest set bit of x
12// This determines the range of responsibility for each node
13function lowbit(x: number): number {
14    return x & -x;
15}
16
17// Update operation: Add delta to position x
18// Time complexity: O(log n)
19function update(x: number, delta: number): void {
20    // Traverse all ancestors of node x
21    while (x <= size) {
22        tree[x] += delta;
23        x += lowbit(x);      // Move to next node that needs updating
24    }
25}
26
27// Query operation: Get prefix sum from index 1 to x
28// Time complexity: O(log n)
29function query(x: number): number {
30    let sum = 0;
31    // Traverse all nodes that contribute to the sum
32    while (x > 0) {
33        sum += tree[x];
34        x -= lowbit(x);      // Move to parent node
35    }
36    return sum;
37}
38
39// Count pairs (i, j) where i < j and nums1[i] - nums1[j] <= nums2[i] - nums2[j] + diff
40// Rearranging: nums1[i] - nums2[i] <= nums1[j] - nums2[j] + diff
41function numberOfPairs(nums1: number[], nums2: number[], diff: number): number {
42    // Create BIT with size 100000 to handle range of possible values
43    initializeBIT(100000);
44  
45    let pairCount = 0;
46  
47    // Process each element
48    for (let i = 0; i < nums1.length; i++) {
49        // Calculate difference for current position
50        const currentDiff = nums1[i] - nums2[i];
51      
52        // Count how many previous elements satisfy the condition
53        // We need to count elements with value <= currentDiff + diff
54        // Add 40000 offset to handle negative values (shift to positive range)
55        pairCount += query(currentDiff + diff + 40000);
56      
57        // Add current difference to the tree for future queries
58        update(currentDiff + 40000, 1);
59    }
60  
61    return pairCount;
62}
63

Time and Space Complexity

Time Complexity: O(n × log n)

The solution iterates through the input arrays once with n elements. For each element:

  • The query operation on the Binary Indexed Tree takes O(log n) time, as it traverses at most log n nodes by repeatedly subtracting lowbit(x) until reaching 0
  • The update operation also takes O(log n) time, as it traverses at most log n nodes by repeatedly adding lowbit(x) until exceeding the tree size

Since we perform both operations for each of the n elements, the total time complexity is O(n × log n).

Space Complexity: O(10^5)

The space complexity is determined by:

  • The Binary Indexed Tree array c which has a fixed size of 10^5 + 1 elements, giving O(10^5) space
  • Other variables like ans, v, a, b use O(1) space

Therefore, the overall space complexity is O(10^5), which is constant with respect to the input size n.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Handling of Negative Values

The Pitfall: The most common mistake is forgetting to handle negative values properly when using a Binary Indexed Tree. Since BIT uses 1-based indexing and requires positive indices, directly using v = nums1[i] - nums2[i] as an index will cause runtime errors when v is negative.

Example of Wrong Implementation:

# WRONG - This will fail when v is negative
for a, b in zip(nums1, nums2):
    v = a - b
    ans += tree.query(v + diff)  # Can be negative!
    tree.update(v, 1)  # Can be negative!

The Solution: Add a sufficient offset to ensure all indices are positive. The code uses 40000 as an offset because:

  • The constraint typically has |nums1[i]|, |nums2[i]| <= 10^4
  • The minimum possible value of v = nums1[i] - nums2[i] is -10^4 - 10^4 = -2*10^4
  • Adding 40000 ensures all indices are positive: -20000 + 40000 = 20000 > 0

2. Off-by-One Error in BIT Size

The Pitfall: Declaring the BIT with insufficient size or not accounting for the offset properly.

Example of Wrong Implementation:

# WRONG - Size might be too small
tree = BinaryIndexedTree(50000)  # Not enough for all possible values with offset

The Solution: Calculate the maximum possible range:

  • With values ranging from -20000 to 20000 and an offset of 40000
  • The actual range becomes 20000 to 60000
  • Add buffer for the diff value: could go up to 60000 + 10000 = 70000
  • Use 100000 to be safe: tree = BinaryIndexedTree(100000)

3. Forgetting to Apply Offset Consistently

The Pitfall: Applying the offset during query but forgetting it during update, or vice versa.

Example of Wrong Implementation:

# WRONG - Inconsistent offset usage
for a, b in zip(nums1, nums2):
    v = a - b
    ans += tree.query(v + diff + 40000)  # Offset applied
    tree.update(v, 1)  # FORGOT offset here!

The Solution: Always apply the same offset to both query and update operations:

# CORRECT
ans += tree.query(v + diff + 40000)
tree.update(v + 40000, 1)

4. Misunderstanding the Inequality Direction

The Pitfall: Confusing which elements should be counted in the query. The transformed inequality v[i] <= v[j] + diff means for each j, we need to count all previous i where v[i] <= v[j] + diff.

Example of Wrong Implementation:

# WRONG - Querying the wrong range
for a, b in zip(nums1, nums2):
    v = a - b
    ans += tree.query(v + 40000)  # Should be v + diff + 40000!
    tree.update(v + 40000, 1)

The Solution: Remember that we're looking for all previous values that are at most v + diff, not just v:

ans += tree.query(v + diff + 40000)  # Correct: v + diff
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What are the two properties the problem needs to have for dynamic programming to be applicable? (Select 2)


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More