Open In App

Magic Balls over Hills

Last Updated : 11 Dec, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Geekland contains a sequence of villages, each with a hill of a certain height. Geek must answer queries about launching an enchanted ball from one village to another.

Geek can launch a ball from village A to village B if every hill strictly between A and B has height less than or equal to the hill at A. If any hill in the middle is taller than the hill at A, the launch fails.

Given an array A[] representing hill heights and a list of queries [L, R], return the number of successful launches.

Example:

Input: A[] = [2, 3, 5, 4, 2, 1, 6], queries[][] = [[3,5], [2, 5], [4, 6]]
Output : 2
Explanation : For query (3,5), the starting height is 5 and the hills between positions 4 and 4 have height 4, which is not higher than 5, so the launch is successful.
For query (2,5), the starting height is 3 but the hill between them has height 5, which blocks the launch, so it fails.
For query (4,6), the starting height is 4 and the hills between them (2 and 1) are all lower than 4, so this launch succeeds.
Therefore, Geek can successfully launch 2 balls.

[Naive Approach] Brute Force - O(n × q) Time and O(1) Space

The idea is to check each query independently by scanning all hills between the starting and ending villages. For a launch from A to B, we first convert the positions to 0-based indexing and then compute the maximum height of the hills strictly between them. If none of these hills are higher than the hill at A, then the launch is counted as successful. Since every query performs a simple linear scan of the intermediate segment, this method is easy to implement and works well for small inputs. However, it becomes slow when the number of hills or queries is large because each query may examine many positions.

C++
#include <iostream>
#include <vector>
using namespace std;

// check if launch from A to B is successful
bool canLaunch(int Apos, int Bpos, vector<int> &A) {

    // convert to 0-based indexing
    Apos--;
    Bpos--;

    // if no hills strictly between A and B → always successful
    if (Apos + 1 > Bpos - 1)
        return true;

    // maximum hill height between Apos+1 and Bpos-1
    int mx = -1;

    // scan the range for maximum height
    for (int i = Apos + 1; i <= Bpos - 1; i++)
        mx = max(mx, A[i]);

    // return true if no hill is strictly higher than A[Apos]
    return (mx <= A[Apos]);
}

int countLaunches(vector<int> &A, vector<vector<int>> &queries) {

    // number of successful launches
    int successful = 0;

    // process each query
    for (auto &q : queries) {

        // Apos → starting village, Bpos → ending village
        int Apos = q[0];
        int Bpos = q[1];

        // if launch is valid, increment count
        if (canLaunch(Apos, Bpos, A))
            successful++;
    }

    // return total successful launches
    return successful;
}

int main() {

    vector<int> A = {2, 3, 5, 4, 2, 1, 6};

    vector<vector<int>> queries = {
        {3, 5},
        {2, 5},
        {4, 6}
    };
    
    cout << countLaunches(A, queries);

    return 0;
}
Java
import java.util.ArrayList;
import java.util.List;

class GFG {

    // check if launch from A → B is successful
    static boolean canLaunch(int Apos, int Bpos, int[] A) {

        // convert to 0-based indexing
        Apos--;
        Bpos--;

        // if no hills strictly between A and B → always successful
        if (Apos + 1 > Bpos - 1)
            return true;

        // maximum hill height between Apos+1 and Bpos-1
        int mx = -1;

        // scan the range for maximum height
        for (int i = Apos + 1; i <= Bpos - 1; i++)
            mx = Math.max(mx, A[i]);

        // return true if no hill is strictly higher than A[Apos]
        return (mx <= A[Apos]);
    }

    static int countLaunches(int[] A, int[][] queries) {

        // number of successful launches
        int successful = 0;

        // process each query
        for (int[] q : queries) {

            // Apos → starting village, Bpos → ending village
            int Apos = q[0];
            int Bpos = q[1];

            // if launch is valid, increment count
            if (canLaunch(Apos, Bpos, A))
                successful++;
        }

        // return total successful launches
        return successful;
    }

    public static void main(String[] args) {

        int[] A = {2, 3, 5, 4, 2, 1, 6};

        int[][] queries = {
            {3, 5},
            {2, 5},
            {4, 6}
        };

        System.out.println(countLaunches(A, queries));
    }
}
Python
# check if launch from A → B is successful
def canLaunch(Apos, Bpos, A):

    # convert to 0-based indexing
    Apos -= 1
    Bpos -= 1

    # no hills strictly between
    if Apos + 1 > Bpos - 1:
        return True

    # maximum hill height between
    mx = -1

    # scan between Apos and Bpos
    for i in range(Apos + 1, Bpos):
        mx = max(mx, A[i])

    # valid if mx <= A[Apos]
    return mx <= A[Apos]

def countLaunches(A, queries):

    successful = 0

    for q in queries:
        Apos, Bpos = q

        if canLaunch(Apos, Bpos, A):
            successful += 1

    return successful


if __name__ == "__main__":

    A = [2, 3, 5, 4, 2, 1, 6]

    queries = [
        [3, 5],
        [2, 5],
        [4, 6]
    ]

    print(countLaunches(A, queries))
C#
using System;
using System.Collections.Generic;

class GFG {

    // check if launch A → B is successful
    static bool canLaunch(int Apos, int Bpos, List<int> A) {

        // convert to 0-based indexing
        Apos--;
        Bpos--;

        // no hills strictly between
        if (Apos + 1 > Bpos - 1)
            return true;

        // maximum between Apos and Bpos
        int mx = -1;

        // scan
        for (int i = Apos + 1; i <= Bpos - 1; i++)
            mx = Math.Max(mx, A[i]);

        // check condition
        return mx <= A[Apos];
    }

    static int countLaunches(List<int> A, List<int[]> queries) {

        int successful = 0;

        foreach (var q in queries) {
            int Apos = q[0];
            int Bpos = q[1];

            if (canLaunch(Apos, Bpos, A))
                successful++;
        }

        return successful;
    }

    static void Main() {

        List<int> A = new List<int> { 2, 3, 5, 4, 2, 1, 6 };

        List<int[]> queries = new List<int[]> {
            new int[] {3, 5},
            new int[] {2, 5},
            new int[] {4, 6}
        };

        Console.WriteLine(countLaunches(A, queries));
    }
}
JavaScript
// check if launch from A → B is successful
function canLaunch(Apos, Bpos, A) {

    // convert to 0-based indexing
    Apos--;
    Bpos--;

    // no hills strictly between
    if (Apos + 1 > Bpos - 1)
        return true;

    // maximum height between
    let mx = -1;

    // scan
    for (let i = Apos + 1; i <= Bpos - 1; i++)
        mx = Math.max(mx, A[i]);

    // valid if mx <= A[Apos]
    return mx <= A[Apos];
}

function countLaunches(A, queries) {

    let successful = 0;

    for (let q of queries) {

        let Apos = q[0];
        let Bpos = q[1];

        if (canLaunch(Apos, Bpos, A))
            successful++;
    }

    return successful;
}

// Driver code
let A = [2, 3, 5, 4, 2, 1, 6];

let queries = [
    [3, 5],
    [2, 5],
    [4, 6]
];

console.log(countLaunches(A, queries));

Output
2

[Expected Approach] Using Segment Tree - O(n + q log n) Time and O(n) Space

The optimal approach is to uses a segment tree to quickly compute the maximum height in any interval. For each launch query between A and B, we only need the highest hill strictly between these two points. Instead of scanning linearly, the segment tree allows retrieving the maximum in O(log n) time. Using this value, we check if it is smaller than the minimum height of the endpoints, which determines whether the launch is successful. This approach significantly reduces total processing time when handling many queries on large hill arrays, making it much faster than the naive solution.

C++
#include <iostream>
#include <vector>
using namespace std;

// build segment tree for maximum values
void buildTree(int idx, int l, int r, vector<int> &seg, vector<int> &A) {

    // leaf node
    if (l == r) {
        seg[idx] = A[l];
        return;
    }

    // midpoint
    int mid = (l + r) / 2;

    // left subtree
    buildTree(2 * idx, l, mid, seg, A);

    // right subtree
    buildTree(2 * idx + 1, mid + 1, r, seg, A);

    // store maximum
    seg[idx] = max(seg[2 * idx], seg[2 * idx + 1]);
}

// query maximum between L and R
int queryMax(int idx, int l, int r, int L, int R, vector<int> &seg) {

    // out of range
    if (r < L || l > R)
        return -1;

    // fully inside range
    if (L <= l && r <= R)
        return seg[idx];

    // midpoint
    int mid = (l + r) / 2;

    // combine results
    return max(queryMax(2 * idx, l, mid, L, R, seg),
               queryMax(2 * idx + 1, mid + 1, r, L, R, seg));
}

// check launch from A -> B
bool canLaunch(int Apos, int Bpos, vector<int> &A, vector<int> &seg, int n) {

    // convert to 0-based
    Apos--;
    Bpos--;

    // no hills between A and B
    if (Apos + 1 > Bpos - 1)
        return true;

    // range maximum between A+1 and B-1
    int mx = queryMax(1, 0, n - 1, Apos + 1, Bpos - 1, seg);

    // VALID RULE → only compare with A[Apos]
    return (mx <= A[Apos]);
}

int countLaunches(vector<int> &A, vector<vector<int>> &queries) {

    int n = A.size();
    vector<int> seg(4 * n);

    // build segment tree
    buildTree(1, 0, n - 1, seg, A);

    int successful = 0;

    // evaluate each query
    for (auto &q : queries) {

        int Apos = q[0];
        int Bpos = q[1];

        if (canLaunch(Apos, Bpos, A, seg, n))
            successful++;
    }

    return successful;
}

int main() {

    vector<int> A = {2, 3, 5, 4, 2, 1, 6};

    vector<vector<int>> queries = {
        {3, 5},
        {2, 5},
        {4, 6}
    };

    cout << countLaunches(A, queries);
    return 0;
}
Java
class GFG {

    // build segment tree
    static void buildTree(int idx, int l, int r, int[] seg, int[] A) {

        // leaf
        if (l == r) {
            seg[idx] = A[l];
            return;
        }

        // mid
        int mid = (l + r) / 2;

        // left subtree
        buildTree(2 * idx, l, mid, seg, A);

        // right subtree
        buildTree(2 * idx + 1, mid + 1, r, seg, A);

        // maximum
        seg[idx] = Math.max(seg[2 * idx], seg[2 * idx + 1]);
    }

    // query maximum
    static int queryMax(int idx, int l, int r, int L, int R, int[] seg) {

        if (r < L || l > R)
            return -1;

        if (L <= l && r <= R)
            return seg[idx];

        int mid = (l + r) / 2;

        return Math.max(
            queryMax(2 * idx, l, mid, L, R, seg),
            queryMax(2 * idx + 1, mid + 1, r, L, R, seg)
        );
    }

    // check launch
    static boolean canLaunch(int Apos, int Bpos, int[] A, int[] seg, int n) {

        Apos--;
        Bpos--;

        if (Apos + 1 > Bpos - 1)
            return true;

        int mx = queryMax(1, 0, n - 1, Apos + 1, Bpos - 1, seg);

        return mx <= A[Apos];
    }

    static int countLaunches(int[] A, int[][] queries) {

        int n = A.length;
        int[] seg = new int[4 * n];

        buildTree(1, 0, n - 1, seg, A);

        int successful = 0;

        for (int[] q : queries)
            if (canLaunch(q[0], q[1], A, seg, n))
                successful++;

        return successful;
    }

    public static void main(String[] args) {

        int[] A = {2, 3, 5, 4, 2, 1, 6};

        int[][] queries = {
            {3, 5},
            {2, 5},
            {4, 6}
        };

        System.out.println(countLaunches(A, queries));
    }
}
Python
def build_tree(idx, l, r, seg, A):
    if l == r:
        seg[idx] = A[l]
        return

    mid = (l + r) // 2

    build_tree(2 * idx, l, mid, seg, A)
    build_tree(2 * idx + 1, mid + 1, r, seg, A)

    seg[idx] = max(seg[2 * idx], seg[2 * idx + 1])


def query_max(idx, l, r, L, R, seg):
    if r < L or l > R:
        return -1

    if L <= l and r <= R:
        return seg[idx]

    mid = (l + r) // 2

    return max(
        query_max(2 * idx, l, mid, L, R, seg),
        query_max(2 * idx + 1, mid + 1, r, L, R, seg)
    )


def canLaunch(Apos, Bpos, A, seg, n):

    Apos -= 1
    Bpos -= 1

    if Apos + 1 > Bpos - 1:
        return True

    mx = query_max(1, 0, n - 1, Apos + 1, Bpos - 1, seg)

    return mx <= A[Apos]


def countLaunches(A, queries):

    n = len(A)
    seg = [0] * (4 * n)

    build_tree(1, 0, n - 1, seg, A)

    successful = 0

    for Apos, Bpos in queries:
        if canLaunch(Apos, Bpos, A, seg, n):
            successful += 1

    return successful

if __name__ == "__main__":
   A = [2, 3, 5, 4, 2, 1, 6]
   queries = [[3, 5], [2, 5], [4, 6]]

print(countLaunches(A, queries))
C#
using System;
using System.Collections.Generic;

class GFG {

    // build segment tree for max
    static void buildTree(int idx, int l, int r, int[] seg, int[] A) {

        // leaf node
        if (l == r) {
            seg[idx] = A[l];
            return;
        }

        // midpoint
        int mid = (l + r) / 2;

        // build left subtree
        buildTree(2 * idx, l, mid, seg, A);

        // build right subtree
        buildTree(2 * idx + 1, mid + 1, r, seg, A);

        // store maximum
        seg[idx] = Math.Max(seg[2 * idx], seg[2 * idx + 1]);
    }

    // query maximum
    static int queryMax(int idx, int l, int r, int L, int R, int[] seg) {

        if (r < L || l > R)
            return -1;

        if (L <= l && r <= R)
            return seg[idx];

        int mid = (l + r) / 2;

        return Math.Max(
            queryMax(2 * idx, l, mid, L, R, seg),
            queryMax(2 * idx + 1, mid + 1, r, L, R, seg)
        );
    }

    // check launch
    static bool canLaunch(int Apos, int Bpos, int[] A, int[] seg, int n) {

        Apos--;
        Bpos--;

        if (Apos + 1 > Bpos - 1)
            return true;

        int mx = queryMax(1, 0, n - 1, Apos + 1, Bpos - 1, seg);

        return mx < Math.Min(A[Apos], A[Bpos]);
    }

    // count launches
    static int countLaunches(int[] A, int[,] queries) {

        int n = A.Length;
        int[] seg = new int[4 * n];

        buildTree(1, 0, n - 1, seg, A);

        int successful = 0;
        int q = queries.GetLength(0);

        for (int i = 0; i < q; i++) {
            int a = queries[i, 0];
            int b = queries[i, 1];

            if (canLaunch(a, b, A, seg, n))
                successful++;
        }

        return successful;
    }

    static void Main() {

        int[] A = {2, 3, 5, 4, 2, 1, 6};

        int[,] queries = {
            {3, 5},
            {2, 5},
            {4, 6}
        };

        Console.WriteLine(countLaunches(A, queries));
    }
}
JavaScript
// build segment tree for max values
function buildTree(idx, l, r, seg, A) {

    // leaf node
    if (l === r) {
        seg[idx] = A[l];
        return;
    }

    // midpoint
    const mid = Math.floor((l + r) / 2);

    // build left subtree
    buildTree(2 * idx, l, mid, seg, A);

    // build right subtree
    buildTree(2 * idx + 1, mid + 1, r, seg, A);

    // store maximum
    seg[idx] = Math.max(seg[2 * idx], seg[2 * idx + 1]);
}

// query maximum in range
function queryMax(idx, l, r, L, R, seg) {

    if (r < L || l > R)
        return -1;

    if (L <= l && r <= R)
        return seg[idx];

    const mid = Math.floor((l + r) / 2);

    return Math.max(
        queryMax(2 * idx, l, mid, L, R, seg),
        queryMax(2 * idx + 1, mid + 1, r, L, R, seg)
    );
}

// check if launch is possible
function canLaunch(Apos, Bpos, A, seg, n) {

    Apos--;
    Bpos--;

    if (Apos + 1 > Bpos - 1)
        return true;

    const mx = queryMax(1, 0, n - 1, Apos + 1, Bpos - 1, seg);

    return mx < Math.min(A[Apos], A[Bpos]);
}

// count successful launches
function countLaunches(A, queries) {

    const n = A.length;
    const seg = new Array(4 * n).fill(0);

    buildTree(1, 0, n - 1, seg, A);

    let successful = 0;

    for (const q of queries)
        if (canLaunch(q[0], q[1], A, seg, n))
            successful++;

    return successful;
}

// Driver code
let A = [2, 3, 5, 4, 2, 1, 6];
let queries = [
    [3, 5],
    [2, 5],
    [4, 6]
];

console.log(countLaunches(A, queries));

Output
2



Article Tags :

Explore