# Design a Food Rating System

- slug: design-a-food-rating-system
- difficulty: Medium

Design a food rating system that can do the following:

*   **Modify** the rating of a food item listed in the system.
*   Return the highest-rated food item for a type of cuisine in the system.

Implement the `FoodRatings` class:

*   `FoodRatings(String[] foods, String[] cuisines, int[] ratings)` Initializes the system. The food items are described by `foods`, `cuisines` and `ratings`, all of which have a length of `n`.
    *   `foods[i]` is the name of the `ith` food,
    *   `cuisines[i]` is the type of cuisine of the `ith` food, and
    *   `ratings[i]` is the initial rating of the `ith` food.
*   `void changeRating(String food, int newRating)` Changes the rating of the food item with the name `food`.
*   `String highestRated(String cuisine)` Returns the name of the food item that has the highest rating for the given type of `cuisine`. If there is a tie, return the item with the **lexicographically smaller** name.

Note that a string `x` is lexicographically smaller than string `y` if `x` comes before `y` in dictionary order, that is, either `x` is a prefix of `y`, or if `i` is the first position such that `x[i] != y[i]`, then `x[i]` comes before `y[i]` in alphabetic order.

**Example 1:**

**Input**
\[ "FoodRatings ",  "highestRated ",  "highestRated ",  "changeRating ",  "highestRated ",  "changeRating ",  "highestRated "\]
\[\[\[ "kimchi ",  "miso ",  "sushi ",  "moussaka ",  "ramen ",  "bulgogi "\], \[ "korean ",  "japanese ",  "japanese ",  "greek ",  "japanese ",  "korean "\], \[9, 12, 8, 15, 14, 7\]\], \[ "korean "\], \[ "japanese "\], \[ "sushi ", 16\], \[ "japanese "\], \[ "ramen ", 16\], \[ "japanese "\]\]
**Output**
\[null,  "kimchi ",  "ramen ", null,  "sushi ", null,  "ramen "\]

**Explanation**
FoodRatings foodRatings = new FoodRatings(\[ "kimchi ",  "miso ",  "sushi ",  "moussaka ",  "ramen ",  "bulgogi "\], \[ "korean ",  "japanese ",  "japanese ",  "greek ",  "japanese ",  "korean "\], \[9, 12, 8, 15, 14, 7\]);
foodRatings.highestRated( "korean "); // return  "kimchi "
                                    //  "kimchi " is the highest rated korean food with a rating of 9.
foodRatings.highestRated( "japanese "); // return  "ramen "
                                      //  "ramen " is the highest rated japanese food with a rating of 14.
foodRatings.changeRating( "sushi ", 16); //  "sushi " now has a rating of 16.
foodRatings.highestRated( "japanese "); // return  "sushi "
                                      //  "sushi " is the highest rated japanese food with a rating of 16.
foodRatings.changeRating( "ramen ", 16); //  "ramen " now has a rating of 16.
foodRatings.highestRated( "japanese "); // return  "ramen "
                                      // Both  "sushi " and  "ramen " have a rating of 16.
                                      // However,  "ramen " is lexicographically smaller than  "sushi ".

**Constraints:**

*   `1 <= n <= 2 * 104`
*   `n == foods.length == cuisines.length == ratings.length`
*   `1 <= foods[i].length, cuisines[i].length <= 10`
*   `foods[i]`, `cuisines[i]` consist of lowercase English letters.
*   `1 <= ratings[i] <= 108`
*   All the strings in `foods` are **distinct**.
*   `food` will be the name of a food item in the system across all calls to `changeRating`.
*   `cuisine` will be a type of cuisine of **at least one** food item in the system across all calls to `highestRated`.
*   At most `2 * 104` calls **in total** will be made to `changeRating` and `highestRated`.

## A solution in Java

```java
import java.util.*;

class FoodRatings {
    HashMap<String, Integer> food_ratings = new HashMap<>();
    HashMap<String, String> highest_rated = new HashMap<>();

    public FoodRatings(String[] foods, String[] cuisines, int[] ratings) {
        for (int i = 0; i < foods.length; ++i) {
            food_ratings.put(foods[i], ratings[i]);
            highest_rated.putIfAbsent(cuisines[i], "");
            if (highest_rated.get(cuisines[i]).equals("") ||
                ratings[i] > food_ratings.get(highest_rated.get(cuisines[i])) ||
                (ratings[i] == food_ratings.get(highest_rated.get(cuisines[i])) && foods[i].compareTo(highest_rated.get(cuisines[i])) < 0)) {
                highest_rated.put(cuisines[i], foods[i]);
            }
        }
    }

    public void changeRating(String food, int newRating) {
        String cuisine = "";
        for (Map.Entry<String, String> it : highest_rated.entrySet()) {
            if (it.getValue().equals(food)) {
                cuisine = it.getKey();
                break;
            }
        }
        food_ratings.put(food, newRating);
        for (Map.Entry<String, Integer> it : food_ratings.entrySet()) {
            if (highest_rated.get(cuisine).equals(food) ||
                (newRating > it.getValue() && cuisine.equals(it.getKey())) ||
                (newRating == it.getValue() && food.compareTo(it.getKey()) < 0)) {
                highest_rated.put(cuisine, food);
            }
        }
    }

    public String highestRated(String cuisine) {
        return highest_rated.get(cuisine);
    }
}
```
The algorithm uses two hash tables (or dictionary/hash map depending on the language). The first one (`food_ratings`) stores the rating for each food in the system, and the second one (`highest_rated`) keeps track of the highest-rated food for each cuisine.

When initializing the object, we iterate through the input lists, set the initial ratings for each food in `food_ratings`, and then update the highest-rated food for each cuisine.

The `changeRating` method first finds the current cuisine for the given food by iterating through the `highest_rated` map. Then, we update the rating of that food in `food_ratings`. Finally, we check if the updated rating food should now be the highest-rated food for its cuisine and update it if necessary.

The `highestRated` method simply returns the highest-rated food (with ties broken by lexicographical order) for the input cuisine by looking up the `highest_rated` map.

## A solution in C++

```cpp
#include <map>
#include <unordered_map>

class FoodRatings {
public:
    FoodRatings(vector<string>& foods, vector<string>& cuisines, vector<int>& ratings) {
        for (int i = 0; i < foods.size(); ++i) {
            food_ratings[foods[i]] = ratings[i];
            if ((highest_rated.find(cuisines[i]) == highest_rated.end()) ||
                (ratings[i] > food_ratings[highest_rated[cuisines[i]]]) ||
                (ratings[i] == food_ratings[highest_rated[cuisines[i]]] && foods[i] < highest_rated[cuisines[i]])) {
                highest_rated[cuisines[i]] = foods[i];
            }
        }
    }

    void changeRating(string food, int newRating) {
        string cuisine;
        for (const auto& it : highest_rated) {
            if (it.second == food) {
                cuisine = it.first;
                break;
            }
        }
        food_ratings[food] = newRating;
        for (const auto& it : food_ratings) {
            if (highest_rated[cuisine] == food ||
                (newRating > it.second && cuisine == it.first) ||
                (newRating == it.second && food < it.first)) {
                highest_rated[cuisine] = food;
            }
        }
    }

    string highestRated(string cuisine) {
        return highest_rated[cuisine];
    }

private:
    unordered_map<string, int> food_ratings;
    unordered_map<string, string> highest_rated;
};
```
The algorithm uses two hash tables (or dictionary/hash map depending on the language). The first one (`food_ratings`) stores the rating for each food in the system, and the second one (`highest_rated`) keeps track of the highest-rated food for each cuisine.

When initializing the object, we iterate through the input lists, set the initial ratings for each food in `food_ratings`, and then update the highest-rated food for each cuisine.

The `changeRating` method first finds the current cuisine for the given food by iterating through the `highest_rated` map. Then, we update the rating of that food in `food_ratings`. Finally, we check if the updated rating food should now be the highest-rated food for its cuisine and update it if necessary.

The `highestRated` method simply returns the highest-rated food (with ties broken by lexicographical order) for the input cuisine by looking up the `highest_rated` map.

## A solution in Python

```python
from collections import defaultdict

class FoodRatings:
    def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]):
        self.food_ratings = {food: rating for food, rating in zip(foods, ratings)}
        self.highest_rated = defaultdict(str)

        for food, cuisine, rating in zip(foods, cuisines, ratings):
            if (not self.highest_rated[cuisine] or
                rating > self.food_ratings[self.highest_rated[cuisine]] or
                (rating == self.food_ratings[self.highest_rated[cuisine]] and food < self.highest_rated[cuisine])):
                self.highest_rated[cuisine] = food

    def changeRating(self, food: str, newRating: int) -> None:
        cuisine = [c for c, f in self.highest_rated.items() if f == food][0]
        self.food_ratings[food] = newRating
        for f, rating in self.food_ratings.items():
            if (self.highest_rated[cuisine] == food or
                (newRating > rating and cuisine == self.food_ratings[f]) or
                (newRating == rating and food < f)):
                self.highest_rated[cuisine] = food

    def highestRated(self, cuisine: str) -> str:
        return self.highest_rated[cuisine]
```
The algorithm uses two hash tables (or dictionary/hash map depending on the language). The first one (`food_ratings`) stores the rating for each food in the system, and the second one (`highest_rated`) keeps track of the highest-rated food for each cuisine.

When initializing the object, we iterate through the input lists, set the initial ratings for each food in `food_ratings`, and then update the highest-rated food for each cuisine.

The `changeRating` method first finds the current cuisine for the given food by iterating through the `highest_rated` map. Then, we update the rating of that food in `food_ratings`. Finally, we check if the updated rating food should now be the highest-rated food for its cuisine and update it if necessary.

The `highestRated` method simply returns the highest-rated food (with ties broken by lexicographical order) for the input cuisine by looking up the `highest_rated` map.

## A solution in Javascript

```javascript
class FoodRatings {
    constructor(foods, cuisines, ratings) {
        this.food_ratings = new Map();
        this.highest_rated = new Map();

        for (let i = 0; i < foods.length; ++i) {
            this.food_ratings.set(foods[i], ratings[i]);
            if (!this.highest_rated.has(cuisines[i]) ||
                ratings[i] > this.food_ratings.get(this.highest_rated.get(cuisines[i])) ||
                (ratings[i] === this.food_ratings.get(this.highest_rated.get(cuisines[i])) && foods[i] < this.highest_rated.get(cuisines[i]))) {
                this.highest_rated.set(cuisines[i], foods[i]);
            }
        }
    }

    changeRating(food, newRating) {
        let cuisine = "";
        for (const [key, value] of this.highest_rated.entries()) {
            if (value === food) {
                cuisine = key;
                break;
            }
        }
        this.food_ratings.set(food, newRating);
        for (const [key, value] of this.food_ratings.entries()) {
            if (this.highest_rated.get(cuisine) === food ||
                (newRating > value && cuisine === key) ||
                (newRating === value && food < key)) {
                this.highest_rated.set(cuisine, food);
            }
        }
    }

    highestRated(cuisine) {
        return this.highest_rated.get(cuisine);
    }
}
```
The algorithm uses two hash tables (or dictionary/hash map depending on the language). The first one (`food_ratings`) stores the rating for each food in the system, and the second one (`highest_rated`) keeps track of the highest-rated food for each cuisine.

When initializing the object, we iterate through the input lists, set the initial ratings for each food in `food_ratings`, and then update the highest-rated food for each cuisine.

The `changeRating` method first finds the current cuisine for the given food by iterating through the `highest_rated` map. Then, we update the rating of that food in `food_ratings`. Finally, we check if the updated rating food should now be the highest-rated food for its cuisine and update it if necessary.

The `highestRated` method simply returns the highest-rated food (with ties broken by lexicographical order) for the input cuisine by looking up the `highest_rated` map.


# Number of Excellent Pairs

- slug: number-of-excellent-pairs
- difficulty: Hard

You are given a **0-indexed** positive integer array `nums` and a positive integer `k`.

A pair of numbers `(num1, num2)` is called **excellent** if the following conditions are satisfied:

*   **Both** the numbers `num1` and `num2` exist in the array `nums`.
*   The sum of the number of set bits in `num1 OR num2` and `num1 AND num2` is greater than or equal to `k`, where `OR` is the bitwise **OR** operation and `AND` is the bitwise **AND** operation.

Return _the number of **distinct** excellent pairs_.

Two pairs `(a, b)` and `(c, d)` are considered distinct if either `a != c` or `b != d`. For example, `(1, 2)` and `(2, 1)` are distinct.

**Note** that a pair `(num1, num2)` such that `num1 == num2` can also be excellent if you have at least **one** occurrence of `num1` in the array.

**Example 1:**

**Input:** nums = \[1,2,3,1\], k = 3
**Output:** 5
**Explanation:** The excellent pairs are the following:
- (3, 3). (3 AND 3) and (3 OR 3) are both equal to (11) in binary. The total number of set bits is 2 + 2 = 4, which is greater than or equal to k = 3.
- (2, 3) and (3, 2). (2 AND 3) is equal to (10) in binary, and (2 OR 3) is equal to (11) in binary. The total number of set bits is 1 + 2 = 3.
- (1, 3) and (3, 1). (1 AND 3) is equal to (01) in binary, and (1 OR 3) is equal to (11) in binary. The total number of set bits is 1 + 2 = 3.
So the number of excellent pairs is 5.

**Example 2:**

**Input:** nums = \[5,1,1\], k = 10
**Output:** 0
**Explanation:** There are no excellent pairs for this array.

**Constraints:**

*   `1 <= nums.length <= 105`
*   `1 <= nums[i] <= 109`
*   `1 <= k <= 60`

## A solution in Java

```java
import java.util.HashMap;
import java.util.Map;

public int countExcellentPairs(int[] nums, int k) {
    HashMap<Integer, Integer> frequency = new HashMap<>();
    for (int num : nums) {
        frequency.put(num, frequency.getOrDefault(num, 0) + 1);
    }

    int count = 0;
    for (Map.Entry<Integer, Integer> entry1 : frequency.entrySet()) {
        for (Map.Entry<Integer, Integer> entry2 : frequency.entrySet()) {
            int num1 = entry1.getKey();
            int num2 = entry2.getKey();
            int setBits = Integer.bitCount(num1 | num2) + Integer.bitCount(num1 & num2);
            if (setBits >= k) {
                count += (num1 == num2) ? entry1.getValue() * (entry1.getValue() - 1) / 2 : entry1.getValue() * entry2.getValue();
            }
        }
    }

    return count;
}
```
The algorithm first counts the frequency of each number in the input array using a hash map. Next, it iterates over all pairs of distinct numbers in the map (including pairs of identical numbers). For each pair, it calculates the number of set bits in both the bitwise OR and bitwise AND of the two numbers. If the sum of set bits is greater than or equal to the target value k, the algorithm increments the count of excellent pairs accordingly. If the pair consists of identical numbers, the count is incremented by the triangular number of the count of that number in the input array, otherwise the count is incremented by the product of the counts of the two numbers.

The time complexity of this algorithm is O(n^2), where n is the number of distinct numbers in the input array. The space complexity is also O(n), as a hash map is used to store the frequency of each number.

## A solution in C++

```cpp
#include <unordered_map>
#include <vector>

int countExcellentPairs(std::vector<int>& nums, int k) {
    std::unordered_map<int, int> frequency;
    for (int num : nums) {
        frequency[num]++;
    }

    int count = 0;
    for (auto it1 = frequency.begin(); it1 != frequency.end(); it1++) {
        for (auto it2 = frequency.begin(); it2 != frequency.end(); it2++) {
            if ((__builtin_popcount(it1->first | it2->first) + __builtin_popcount(it1->first & it2->first)) >= k) {
                count += (it1->first == it2->first) ? it1->second * (it1->second - 1) / 2 : it1->second * it2->second;
            }
        }
    }

    return count;
}
```
The algorithm first counts the frequency of each number in the input array using a hash map. Next, it iterates over all pairs of distinct numbers in the map (including pairs of identical numbers). For each pair, it calculates the number of set bits in both the bitwise OR and bitwise AND of the two numbers. If the sum of set bits is greater than or equal to the target value k, the algorithm increments the count of excellent pairs accordingly. If the pair consists of identical numbers, the count is incremented by the triangular number of the count of that number in the input array, otherwise the count is incremented by the product of the counts of the two numbers.

The time complexity of this algorithm is O(n^2), where n is the number of distinct numbers in the input array. The space complexity is also O(n), as a hash map is used to store the frequency of each number.

## A solution in Python

```python
from collections import Counter

def countExcellentPairs(nums, k):
    frequency = Counter(nums)
    count = 0
    for num1, freq1 in frequency.items():
        for num2, freq2 in frequency.items():
            setBits = bin(num1 | num2).count('1') + bin(num1 & num2).count('1')
            if setBits >= k:
                count += freq1 * freq2 if num1 != num2 else freq1 * (freq1 - 1) // 2
    return count
```
The algorithm first counts the frequency of each number in the input array using a hash map. Next, it iterates over all pairs of distinct numbers in the map (including pairs of identical numbers). For each pair, it calculates the number of set bits in both the bitwise OR and bitwise AND of the two numbers. If the sum of set bits is greater than or equal to the target value k, the algorithm increments the count of excellent pairs accordingly. If the pair consists of identical numbers, the count is incremented by the triangular number of the count of that number in the input array, otherwise the count is incremented by the product of the counts of the two numbers.

The time complexity of this algorithm is O(n^2), where n is the number of distinct numbers in the input array. The space complexity is also O(n), as a hash map is used to store the frequency of each number.

## A solution in Javascript

```javascript
function countExcellentPairs(nums, k) {
    let frequency = new Map();
    for (let num of nums) {
        frequency.set(num, (frequency.get(num) || 0) + 1);
    }

    let count = 0;
    for (let [num1, freq1] of frequency.entries()) {
        for (let [num2, freq2] of frequency.entries()) {
            let setBits = (num1 | num2).toString(2).replace(/0/g, '').length + (num1 & num2).toString(2).replace(/0/g, '').length;
            if (setBits >= k) {
                count += (num1 === num2) ? freq1 * (freq1 - 1) / 2 : freq1 * freq2;
            }
        }
    }

    return count;
}
```
The algorithm first counts the frequency of each number in the input array using a hash map. Next, it iterates over all pairs of distinct numbers in the map (including pairs of identical numbers). For each pair, it calculates the number of set bits in both the bitwise OR and bitwise AND of the two numbers. If the sum of set bits is greater than or equal to the target value k, the algorithm increments the count of excellent pairs accordingly. If the pair consists of identical numbers, the count is incremented by the triangular number of the count of that number in the input array, otherwise the count is incremented by the product of the counts of the two numbers.

The time complexity of this algorithm is O(n^2), where n is the number of distinct numbers in the input array. The space complexity is also O(n), as a hash map is used to store the frequency of each number.


# Maximum Number of Books You Can Take

- slug: maximum-number-of-books-you-can-take
- difficulty: Hard

You are given a **0-indexed** integer array `books` of length `n` where `books[i]` denotes the number of books on the `ith` shelf of a bookshelf.

You are going to take books from a **contiguous** section of the bookshelf spanning from `l` to `r` where `0 <= l <= r < n`. For each index `i` in the range `l <= i < r`, you must take **strictly fewer** books from shelf `i` than shelf `i + 1`.

Return _the **maximum** number of books you can take from the bookshelf._

**Example 1:**

**Input:** books = \[8,5,2,7,9\]
**Output:** 19
**Explanation:**
- Take 1 book from shelf 1.
- Take 2 books from shelf 2.
- Take 7 books from shelf 3.
- Take 9 books from shelf 4.
You have taken 19 books, so return 19.
It can be proven that 19 is the maximum number of books you can take.

**Example 2:**

**Input:** books = \[7,0,3,4,5\]
**Output:** 12
**Explanation:**
- Take 3 books from shelf 2.
- Take 4 books from shelf 3.
- Take 5 books from shelf 4.
You have taken 12 books so return 12.
It can be proven that 12 is the maximum number of books you can take.

**Example 3:**

**Input:** books = \[8,2,3,7,3,4,0,1,4,3\]
**Output:** 13
**Explanation:**
- Take 1 book from shelf 0.
- Take 2 books from shelf 1.
- Take 3 books from shelf 2.
- Take 7 books from shelf 3.
You have taken 13 books so return 13.
It can be proven that 13 is the maximum number of books you can take.

**Constraints:**

*   `1 <= books.length <= 105`
*   `0 <= books[i] <= 105`

## A solution in Java

```java
int maxBooks(int[] books) {
    int n = books.length;
    int max_books = 0;

    for (int l = 0; l < n; ++l) {
        int r = l + 1, books_taken = books[l], min_taken = books[l];
        max_books = Math.max(max_books, books_taken);

        while (r < n) {
            min_taken = Math.min(min_taken, books[r]);
            books_taken += min_taken;
            max_books = Math.max(max_books, books_taken);

            if (min_taken <= 1) break; // Can't decrease further
            ++r;
        }
    }

    return max_books;
}
```
The algorithm starts with a loop that iterates through the books array from the first index to the last index (0 to n-1). For each index `l` (left), it initializes the variables `books_taken` & `min_taken` to the value of books[l]. Then, it iterates from index `l+1` (right) to the last index of the books array. In the inner loop, updates `min_taken` with the minimum value between `min_taken` and books[r] and add the value of `min_taken` to `books_taken`. It updates the value of `max_books` with the maximum value between `max_books` and `books_taken`. These steps are repeated for every pair of l and r such that 0 <= l <= r < n.

The algorithm aims to find the maximum number of books taken from a contiguous section of the bookshelf, following the constraint "strictly fewer books from shelf i than shelf i+1". The loop will identify the optimal segments in every iteration/update `max_books`.

The breaks condition (`if min_taken <= 1`) is checked, and if it's `True`, the loop exits since the number of books can't decrease further, according to the constraints.

Finally, the maximum number of books taken is returned as the answer.

## A solution in C++

```cpp
int maxBooks(vector<int>& books) {
    int n = books.size();
    int max_books = 0;

    for (int l = 0; l < n; ++l) {
        int r = l + 1, books_taken = books[l], min_taken = books[l];
        max_books = max(max_books, books_taken);

        while (r < n) {
            min_taken = min(min_taken, books[r]);
            books_taken += min_taken;
            max_books = max(max_books, books_taken);

            if (min_taken <= 1) break; // Can't decrease further
            ++r;
        }
    }

    return max_books;
}
```
The algorithm starts with a loop that iterates through the books array from the first index to the last index (0 to n-1). For each index `l` (left), it initializes the variables `books_taken` & `min_taken` to the value of books[l]. Then, it iterates from index `l+1` (right) to the last index of the books array. In the inner loop, updates `min_taken` with the minimum value between `min_taken` and books[r] and add the value of `min_taken` to `books_taken`. It updates the value of `max_books` with the maximum value between `max_books` and `books_taken`. These steps are repeated for every pair of l and r such that 0 <= l <= r < n.

The algorithm aims to find the maximum number of books taken from a contiguous section of the bookshelf, following the constraint "strictly fewer books from shelf i than shelf i+1". The loop will identify the optimal segments in every iteration/update `max_books`.

The breaks condition (`if min_taken <= 1`) is checked, and if it's `True`, the loop exits since the number of books can't decrease further, according to the constraints.

Finally, the maximum number of books taken is returned as the answer.

## A solution in Python

```python
def max_books(books):
    n = len(books)
    max_books = 0

    for l in range(n):
        r = l + 1
        books_taken = books[l]
        min_taken = books[l]
        max_books = max(max_books, books_taken)

        while r < n:
            min_taken = min(min_taken, books[r])
            books_taken += min_taken
            max_books = max(max_books, books_taken)

            if min_taken <= 1:
                break  # Can't decrease further
            r += 1

    return max_books
```
The algorithm starts with a loop that iterates through the books array from the first index to the last index (0 to n-1). For each index `l` (left), it initializes the variables `books_taken` & `min_taken` to the value of books[l]. Then, it iterates from index `l+1` (right) to the last index of the books array. In the inner loop, updates `min_taken` with the minimum value between `min_taken` and books[r] and add the value of `min_taken` to `books_taken`. It updates the value of `max_books` with the maximum value between `max_books` and `books_taken`. These steps are repeated for every pair of l and r such that 0 <= l <= r < n.

The algorithm aims to find the maximum number of books taken from a contiguous section of the bookshelf, following the constraint "strictly fewer books from shelf i than shelf i+1". The loop will identify the optimal segments in every iteration/update `max_books`.

The breaks condition (`if min_taken <= 1`) is checked, and if it's `True`, the loop exits since the number of books can't decrease further, according to the constraints.

Finally, the maximum number of books taken is returned as the answer.

## A solution in Javascript

```javascript
function maxBooks(books) {
    const n = books.length;
    let max_books = 0;

    for (let l = 0; l < n; ++l) {
        let r = l + 1;
        let books_taken = books[l];
        let min_taken = books[l];
        max_books = Math.max(max_books, books_taken);

        while (r < n) {
            min_taken = Math.min(min_taken, books[r]);
            books_taken += min_taken;
            max_books = Math.max(max_books, books_taken);

            if (min_taken <= 1) break; // Can't decrease further
            ++r;
        }
    }

    return max_books;
}
```
The algorithm starts with a loop that iterates through the books array from the first index to the last index (0 to n-1). For each index `l` (left), it initializes the variables `books_taken` & `min_taken` to the value of books[l]. Then, it iterates from index `l+1` (right) to the last index of the books array. In the inner loop, updates `min_taken` with the minimum value between `min_taken` and books[r] and add the value of `min_taken` to `books_taken`. It updates the value of `max_books` with the maximum value between `max_books` and `books_taken`. These steps are repeated for every pair of l and r such that 0 <= l <= r < n.

The algorithm aims to find the maximum number of books taken from a contiguous section of the bookshelf, following the constraint "strictly fewer books from shelf i than shelf i+1". The loop will identify the optimal segments in every iteration/update `max_books`.

The breaks condition (`if min_taken <= 1`) is checked, and if it's `True`, the loop exits since the number of books can't decrease further, according to the constraints.

Finally, the maximum number of books taken is returned as the answer.


# Make Array Zero by Subtracting Equal Amounts

- slug: make-array-zero-by-subtracting-equal-amounts
- difficulty: Easy

You are given a non-negative integer array `nums`. In one operation, you must:

*   Choose a positive integer `x` such that `x` is less than or equal to the **smallest non-zero** element in `nums`.
*   Subtract `x` from every **positive** element in `nums`.

Return _the **minimum** number of operations to make every element in_ `nums` _equal to_ `0`.

**Example 1:**

**Input:** nums = \[1,5,0,3,5\]
**Output:** 3
**Explanation:**
In the first operation, choose x = 1. Now, nums = \[0,4,0,2,4\].
In the second operation, choose x = 2. Now, nums = \[0,2,0,0,2\].
In the third operation, choose x = 2. Now, nums = \[0,0,0,0,0\].

**Example 2:**

**Input:** nums = \[0\]
**Output:** 0
**Explanation:** Each element in nums is already 0 so no operations are needed.

**Constraints:**

*   `1 <= nums.length <= 100`
*   `0 <= nums[i] <= 100`

## A solution in Java

```java
public int minOperations(int[] nums) {
    int count = 0;
    Arrays.sort(nums);

    for (int i = 0; i < nums.length; i++) {
        if (nums[i] > 0) {
            count += nums.length - (Arrays.binarySearch(nums, i, nums.length, nums[i] + 1) < 0 ? -Arrays.binarySearch(nums, i, nums.length, nums[i] + 1) - 1 : Arrays.binarySearch(nums, i, nums.length, nums[i] + 1));
            i = (Arrays.binarySearch(nums, nums[i] + 1) < 0 ? -Arrays.binarySearch(nums, nums[i] + 1) - 2 : Arrays.binarySearch(nums, nums[i] + 1)) - 1;
        }
    }

    return count;
}
```
1. Sort the given array in non-descending order.
2. Initialize a variable count to store the number of operations.
3. Iterate through the sorted array.
   - If the current element is positive, find the count of elements with the same value.
   - Add this count to the overall count and update i to skip these elements.
   - If the current element is not positive, just move to the next element.
4. Return the overall count (number of operations).

This algorithm works because after sorting the array, we traverse it and count distinct positive numbers. Each distinct positive number corresponds to a unique operation that reduces it to zero while also reducing any larger numbers in the array. Since the operation only affects positive numbers, we can skip the zero elements. By the end of the algorithm, we have counted the minimum number of operations required to make all elements equal to zero.

## A solution in C++

```cpp
int minOperations(vector<int>& nums) {
    int count = 0;
    std::sort(nums.begin(), nums.end());

    for (int i = 0; i < nums.size(); ++i) {
        if (nums[i] > 0) {
            count += nums.end() - (std::upper_bound(nums.begin() + i, nums.end(), nums[i]));
            i = (std::upper_bound(nums.begin(), nums.end(), nums[i])) - nums.begin() - 1;
        }
    }

    return count;
}
```
1. Sort the given array in non-descending order.
2. Initialize a variable count to store the number of operations.
3. Iterate through the sorted array.
   - If the current element is positive, find the count of elements with the same value.
   - Add this count to the overall count and update i to skip these elements.
   - If the current element is not positive, just move to the next element.
4. Return the overall count (number of operations).

This algorithm works because after sorting the array, we traverse it and count distinct positive numbers. Each distinct positive number corresponds to a unique operation that reduces it to zero while also reducing any larger numbers in the array. Since the operation only affects positive numbers, we can skip the zero elements. By the end of the algorithm, we have counted the minimum number of operations required to make all elements equal to zero.

## A solution in Python

```python
def minOperations(nums):
    count = 0
    nums.sort()
    i = 0

    while i < len(nums):
        if nums[i] > 0:
            target = nums[i]
            while i < len(nums) and nums[i] == target:
                i += 1
            count += 1
        else:
            i += 1

    return count
```
1. Sort the given array in non-descending order.
2. Initialize a variable count to store the number of operations.
3. Iterate through the sorted array.
   - If the current element is positive, find the count of elements with the same value.
   - Add this count to the overall count and update i to skip these elements.
   - If the current element is not positive, just move to the next element.
4. Return the overall count (number of operations).

This algorithm works because after sorting the array, we traverse it and count distinct positive numbers. Each distinct positive number corresponds to a unique operation that reduces it to zero while also reducing any larger numbers in the array. Since the operation only affects positive numbers, we can skip the zero elements. By the end of the algorithm, we have counted the minimum number of operations required to make all elements equal to zero.

## A solution in Javascript

```javascript
function minOperations(nums) {
    let count = 0;
    nums.sort((a, b) => a - b);

    for (let i = 0; i < nums.length; i++) {
        if (nums[i] > 0) {
            const target = nums[i];
            while (i < nums.length && nums[i] === target) {
                i++;
            }
            count++;
        }
    }

    return count;
}
```
1. Sort the given array in non-descending order.
2. Initialize a variable count to store the number of operations.
3. Iterate through the sorted array.
   - If the current element is positive, find the count of elements with the same value.
   - Add this count to the overall count and update i to skip these elements.
   - If the current element is not positive, just move to the next element.
4. Return the overall count (number of operations).

This algorithm works because after sorting the array, we traverse it and count distinct positive numbers. Each distinct positive number corresponds to a unique operation that reduces it to zero while also reducing any larger numbers in the array. Since the operation only affects positive numbers, we can skip the zero elements. By the end of the algorithm, we have counted the minimum number of operations required to make all elements equal to zero.


# Maximum Number of Groups Entering a Competition

- slug: maximum-number-of-groups-entering-a-competition
- difficulty: Medium

You are given a positive integer array `grades` which represents the grades of students in a university. You would like to enter **all** these students into a competition in **ordered** non-empty groups, such that the ordering meets the following conditions:

*   The sum of the grades of students in the `ith` group is **less than** the sum of the grades of students in the `(i + 1)th` group, for all groups (except the last).
*   The total number of students in the `ith` group is **less than** the total number of students in the `(i + 1)th` group, for all groups (except the last).

Return _the **maximum** number of groups that can be formed_.

**Example 1:**

**Input:** grades = \[10,6,12,7,3,5\]
**Output:** 3
**Explanation:** The following is a possible way to form 3 groups of students:
- 1st group has the students with grades = \[12\]. Sum of grades: 12. Student count: 1
- 2nd group has the students with grades = \[6,7\]. Sum of grades: 6 + 7 = 13. Student count: 2
- 3rd group has the students with grades = \[10,3,5\]. Sum of grades: 10 + 3 + 5 = 18. Student count: 3
It can be shown that it is not possible to form more than 3 groups.

**Example 2:**

**Input:** grades = \[8,8\]
**Output:** 1
**Explanation:** We can only form 1 group, since forming 2 groups would lead to an equal number of students in both groups.

**Constraints:**

*   `1 <= grades.length <= 105`
*   `1 <= grades[i] <= 105`

## A solution in Java

```java
int max_groups(int[] grades) {
    int len = grades.length;
    int[] dp = new int[len];
    Arrays.fill(dp, 1);

    Arrays.sort(grades);

    for (int i = 0; i < len; ++i) {
        for (int j = 0; j < i; ++j) {
            if (grades[i] + dp[j] * grades[j] > (i+1) * grades[j]) {
                dp[i] = dp[j] + 1;
            }
        }
    }

    return dp[len - 1];
}
```
First, we sort the `grades` array in ascending order. Then, we create an array `dp` of the same length initialized with all ones because the minimum number of groups for any element is 1.

We iterate through the `grades` in two nested loops where the inner loop iterates up to the current index of the outer loop. Using dynamic programming, we check if `(grades[i] + dp[j] * grades[j])` is greater than `((i+1) * grades[j])`. If it is, we update `dp[i] = dp[j] + 1`. When the last element of `dp` is reached, this will contain the maximum number of groups possible.

## A solution in C++

```cpp
int max_groups(vector<int>& grades) {
    int len = grades.size();
    vector<int> dp(len, 1);

    sort(grades.begin(), grades.end());

    for (int i = 0; i < len; ++i) {
        for (int j = 0; j < i; ++j) {
            if (grades[i] + dp[j] * grades[j] > (i+1) * grades[j]) {
                dp[i] = dp[j] + 1;
            }
        }
    }

    return dp[len-1];
}
```
First, we sort the `grades` array in ascending order. Then, we create an array `dp` of the same length initialized with all ones because the minimum number of groups for any element is 1.

We iterate through the `grades` in two nested loops where the inner loop iterates up to the current index of the outer loop. Using dynamic programming, we check if `(grades[i] + dp[j] * grades[j])` is greater than `((i+1) * grades[j])`. If it is, we update `dp[i] = dp[j] + 1`. When the last element of `dp` is reached, this will contain the maximum number of groups possible.

## A solution in Python

```python
def max_groups(grades):
    grades.sort()

    dp = [1] * len(grades)

    for i in range(len(grades)):
        for j in range(i):
            if grades[i] + dp[j] * grades[j] > (i+1) * grades[j]:
                dp[i] = dp[j] + 1

    return dp[-1]
```
First, we sort the `grades` array in ascending order. Then, we create an array `dp` of the same length initialized with all ones because the minimum number of groups for any element is 1.

We iterate through the `grades` in two nested loops where the inner loop iterates up to the current index of the outer loop. Using dynamic programming, we check if `(grades[i] + dp[j] * grades[j])` is greater than `((i+1) * grades[j])`. If it is, we update `dp[i] = dp[j] + 1`. When the last element of `dp` is reached, this will contain the maximum number of groups possible.

## A solution in Javascript

```javascript
function max_groups(grades) {
    grades.sort((a, b) => a - b);

    const dp = Array(grades.length).fill(1);

    for (let i = 0; i < grades.length; ++i) {
        for (let j = 0; j < i; ++j) {
            if (grades[i] + dp[j] * grades[j] > (i+1) * grades[j]) {
                dp[i] = dp[j] + 1;
            }
        }
    }

    return dp[grades.length - 1];
}
```
First, we sort the `grades` array in ascending order. Then, we create an array `dp` of the same length initialized with all ones because the minimum number of groups for any element is 1.

We iterate through the `grades` in two nested loops where the inner loop iterates up to the current index of the outer loop. Using dynamic programming, we check if `(grades[i] + dp[j] * grades[j])` is greater than `((i+1) * grades[j])`. If it is, we update `dp[i] = dp[j] + 1`. When the last element of `dp` is reached, this will contain the maximum number of groups possible.


# Find Closest Node to Given Two Nodes

- slug: find-closest-node-to-given-two-nodes
- difficulty: Medium

You are given a **directed** graph of `n` nodes numbered from `0` to `n - 1`, where each node has **at most one** outgoing edge.

The graph is represented with a given **0-indexed** array `edges` of size `n`, indicating that there is a directed edge from node `i` to node `edges[i]`. If there is no outgoing edge from `i`, then `edges[i] == -1`.

You are also given two integers `node1` and `node2`.

Return _the **index** of the node that can be reached from both_ `node1` _and_ `node2`_, such that the **maximum** between the distance from_ `node1` _to that node, and from_ `node2` _to that node is **minimized**_. If there are multiple answers, return the node with the **smallest** index, and if no possible answer exists, return `-1`.

Note that `edges` may contain cycles.

**Example 1:**

**Input:** edges = \[2,2,3,-1\], node1 = 0, node2 = 1
**Output:** 2
**Explanation:** The distance from node 0 to node 2 is 1, and the distance from node 1 to node 2 is 1.
The maximum of those two distances is 1. It can be proven that we cannot get a node with a smaller maximum distance than 1, so we return node 2.

**Example 2:**

**Input:** edges = \[1,2,-1\], node1 = 0, node2 = 2
**Output:** 2
**Explanation:** The distance from node 0 to node 2 is 2, and the distance from node 2 to itself is 0.
The maximum of those two distances is 2. It can be proven that we cannot get a node with a smaller maximum distance than 2, so we return node 2.

**Constraints:**

*   `n == edges.length`
*   `2 <= n <= 105`
*   `-1 <= edges[i] < n`
*   `edges[i] != i`
*   `0 <= node1, node2 < n`

## A solution in Java

```java
public int get_index_with_min_max_distance(int[] edges, int node1, int node2) {
    int[] dist1 = new int[edges.length];
    int[] dist2 = new int[edges.length];

    Arrays.fill(dist1, -1);
    Arrays.fill(dist2, -1);

    dist1[node1] = 0;
    dist2[node2] = 0;
    for (int i = 0; i < edges.length; ++i) {
        if (dist1[i] != -1 && edges[i] != -1) {
            if (dist1[edges[i]] == -1) {
                dist1[edges[i]] = dist1[i] + 1;
            } else {
                dist1[edges[i]] = Math.min(dist1[edges[i]], dist1[i] + 1);
            }
        }

        if (dist2[i] != -1 && edges[i] != -1) {
        ...
    ...

    return ans;
}
```
We first create arrays `dist1` and `dist2` of the same size as `edges`, initialize them with `-1`, and set `dist[node1] = 0` and `dist[node2] = 0`. These arrays keep track of the shortest distances from `node1` and `node2`, respectively. Then, we iterate through the nodes in the graph, and for each node `i` that has an outgoing edge and is reachable from `node1` or `node2`, we update `dist1[edges[i]]` and `dist2[edges[i]]` to be the minimum of their current value and the distance from the previous node plus 1.

Finally, we iterate through the nodes again to find the node with the minimum maximum distance from `node1` and `node2`. For each node `i` that has a valid distance from both `node1` and `node2`, we compute the maximum distance `max_dist1 = max(dist1[i], dist2[i])`. If this maximum distance is smaller than the minimum maximum distance seen so far, we update the answer to be node `i`.

## A solution in C++

```cpp
int get_index_with_min_max_distance(vector<int>& edges, int node1, int node2) {
    vector<int> dist1(edges.size(), -1), dist2(edges.size(), -1);

    dist1[node1] = 0;
    dist2[node2] = 0;
    for (int i = 0; i < edges.size(); ++i) {
        if (dist1[i] != -1 && edges[i] != -1) {
            if (dist1[edges[i]] == -1) {
                dist1[edges[i]] = dist1[i] + 1;
            } else {
                dist1[edges[i]] = min(dist1[edges[i]], dist1[i] + 1);
            }
        }

        if (dist2[i] != -1 && edges[i] != -1) {
            if (dist2[edges[i]] == -1) {
                dist2[edges[i]] = dist2[i] + 1;
            } else {
                dist2[edges[i]] = min(dist2[edges[i]], dist2[i] + 1);
            }
        }
    }

    int ans = -1;
    int min_max_dist = INT_MAX;
    for (int i = 0; i < edges.size(); ++i) {
        if (dist1[i] != -1 && dist2[i] != -1) {
            int max_dist = max(dist1[i], dist2[i]);
            if (max_dist < min_max_dist) {
                min_max_dist = max_dist;
                ans = i;
            }
        }
    }

    return ans;
}
```
We first create arrays `dist1` and `dist2` of the same size as `edges`, initialize them with `-1`, and set `dist[node1] = 0` and `dist[node2] = 0`. These arrays keep track of the shortest distances from `node1` and `node2`, respectively. Then, we iterate through the nodes in the graph, and for each node `i` that has an outgoing edge and is reachable from `node1` or `node2`, we update `dist1[edges[i]]` and `dist2[edges[i]]` to be the minimum of their current value and the distance from the previous node plus 1.

Finally, we iterate through the nodes again to find the node with the minimum maximum distance from `node1` and `node2`. For each node `i` that has a valid distance from both `node1` and `node2`, we compute the maximum distance `max_dist1 = max(dist1[i], dist2[i])`. If this maximum distance is smaller than the minimum maximum distance seen so far, we update the answer to be node `i`.

## A solution in Python

```python
def get_index_with_min_max_distance(edges, node1, node2):
    dist1 = [-1] * len(edges)
    dist2 = [-1] * len(edges)

    dist1[node1] = 0
    dist2[node2] = 0
    for i in range(len(edges)):
        if dist1[i] != -1 and edges[i] != -1:
            if dist1[edges[i]] == -1:
                dist1[edges[i]] = dist1[i] + 1
            else:
                dist1[edges[i]] = min(dist1[edges[i]], dist1[i] + 1)

        if dist2[i] != -1 and edges[i] != -1:
            if dist2[edges[i]] == -1:
                dist2[edges[i]] = dist2[i] + 1
            else:
                dist2[edges[i]] = min(dist2[edges[i]], dist2[i] + 1)

    ans = -1
    min_max_dist = float("inf")
    for i in range(len(edges)):
        if dist1[i] != -1 and dist2[i] != -1:
            max_dist = max(dist1[i], dist2[i])
            if max_dist < min_max_dist:
                min_max_dist = max_dist
                ans = i

    return ans
```
We first create arrays `dist1` and `dist2` of the same size as `edges`, initialize them with `-1`, and set `dist[node1] = 0` and `dist[node2] = 0`. These arrays keep track of the shortest distances from `node1` and `node2`, respectively. Then, we iterate through the nodes in the graph, and for each node `i` that has an outgoing edge and is reachable from `node1` or `node2`, we update `dist1[edges[i]]` and `dist2[edges[i]]` to be the minimum of their current value and the distance from the previous node plus 1.

Finally, we iterate through the nodes again to find the node with the minimum maximum distance from `node1` and `node2`. For each node `i` that has a valid distance from both `node1` and `node2`, we compute the maximum distance `max_dist1 = max(dist1[i], dist2[i])`. If this maximum distance is smaller than the minimum maximum distance seen so far, we update the answer to be node `i`.

## A solution in Javascript

```javascript
function get_index_with_min_max_distance(edges, node1, node2) {
    let dist1 = new Array(edges.length).fill(-1);
    let dist2 = new Array(edges.length).fill(-1);

    dist1[node1] = 0;
    dist2[node2] = 0;
    for (let i = 0; i < edges.length; ++i) {
        if (dist1[i] !== -1 && edges[i] !== -1) {
            if (dist1[edges[i]] === -1) {
                dist1[edges[i]] = dist1[i] + 1;
            } else {
                dist1[edges[i]] = Math.min(dist1[edges[i]], dist1[i] + 1);
            }
        }

        if (dist2[i] !== -1 && edges[i] !== -1) {
            if (dist2[edges[i]] === -1) {
                dist2[edges[i]] = dist2[i] + 1;
            } else {
                dist2[edges[i]] = Math.min(dist2[edges[i]], dist2[i] + 1);
            }
        }
    }

    let ans = -1;
    let min_max_dist = Infinity;
    for (let i = 0; i < edges.length; ++i) {
        if (dist1[i] !== -1 && dist2[i] !== -1) {
            let max_dist = Math.max(dist1[i], dist2[i]);
            if (max_dist < min_max_dist) {
                min_max_dist = max_dist;
                ans = i;
            }
        }
    }

    return ans;
}
```
We first create arrays `dist1` and `dist2` of the same size as `edges`, initialize them with `-1`, and set `dist[node1] = 0` and `dist[node2] = 0`. These arrays keep track of the shortest distances from `node1` and `node2`, respectively. Then, we iterate through the nodes in the graph, and for each node `i` that has an outgoing edge and is reachable from `node1` or `node2`, we update `dist1[edges[i]]` and `dist2[edges[i]]` to be the minimum of their current value and the distance from the previous node plus 1.

Finally, we iterate through the nodes again to find the node with the minimum maximum distance from `node1` and `node2`. For each node `i` that has a valid distance from both `node1` and `node2`, we compute the maximum distance `max_dist1 = max(dist1[i], dist2[i])`. If this maximum distance is smaller than the minimum maximum distance seen so far, we update the answer to be node `i`.


# Longest Cycle in a Graph

- slug: longest-cycle-in-a-graph
- difficulty: Hard

You are given a **directed** graph of `n` nodes numbered from `0` to `n - 1`, where each node has **at most one** outgoing edge.

The graph is represented with a given **0-indexed** array `edges` of size `n`, indicating that there is a directed edge from node `i` to node `edges[i]`. If there is no outgoing edge from node `i`, then `edges[i] == -1`.

Return _the length of the **longest** cycle in the graph_. If no cycle exists, return `-1`.

A cycle is a path that starts and ends at the **same** node.

**Example 1:**

**Input:** edges = \[3,3,4,2,3\]
**Output:** 3
**Explanation:** The longest cycle in the graph is the cycle: 2 -> 4 -> 3 -> 2.
The length of this cycle is 3, so 3 is returned.

**Example 2:**

**Input:** edges = \[2,-1,3,1\]
**Output:** -1
**Explanation:** There are no cycles in this graph.

**Constraints:**

*   `n == edges.length`
*   `2 <= n <= 105`
*   `-1 <= edges[i] < n`
*   `edges[i] != i`

## A solution in Java

```java
public int findLongestCycle(int[] edges) {
    int n = edges.length;
    boolean[] visited = new boolean[n];
    int longest_cycle = -1;

    for (int i = 0; i < n; ++i) {
        if (!visited[i]) {
            int length = 0;
            int node = i;

            while (!visited[node] && edges[node] != -1) {
                visited[node] = true;
                length++;
                node = edges[node];
            }

            if (edges[node] == i) {
                longest_cycle = Math.max(longest_cycle, length);
            }
        }
    }

    return longest_cycle;
}
```
The algorithm iterates through each node in the graph. For each unvisited node, we start a loop that increases the path length on every iteration until the current node is visited or there is no outgoing edge from the current node. If the next node is equal to the starting node, we found a cycle, which update the longest cycle if it is greater than the current longest cycle.

## A solution in C++

```cpp
int findLongestCycle(vector<int>& edges) {
    int n = edges.size();
    vector<bool> visited(n, false);
    int longest_cycle = -1;

    for (int i = 0; i < n; ++i) {
        if (!visited[i]) {
            int length = 0;
            int node = i;

            while (!visited[node] && edges[node] != -1) {
                visited[node] = true;
                length++;
                node = edges[node];
            }

            if (edges[node] == i) {
                longest_cycle = max(longest_cycle, length);
            }
        }
    }

    return longest_cycle;
}
```
The algorithm iterates through each node in the graph. For each unvisited node, we start a loop that increases the path length on every iteration until the current node is visited or there is no outgoing edge from the current node. If the next node is equal to the starting node, we found a cycle, which update the longest cycle if it is greater than the current longest cycle.

## A solution in Python

```python
def findLongestCycle(edges):
    n = len(edges)
    visited = [False] * n
    longest_cycle = -1

    for i in range(n):
        if not visited[i]:
            length = 0
            node = i

            while not visited[node] and edges[node] != -1:
                visited[node] = True
                length += 1
                node = edges[node]

            if edges[node] == i:
                longest_cycle = max(longest_cycle, length)

    return longest_cycle
```
The algorithm iterates through each node in the graph. For each unvisited node, we start a loop that increases the path length on every iteration until the current node is visited or there is no outgoing edge from the current node. If the next node is equal to the starting node, we found a cycle, which update the longest cycle if it is greater than the current longest cycle.

## A solution in Javascript

```javascript
function findLongestCycle(edges) {
    const n = edges.length;
    const visited = new Array(n).fill(false);
    let longest_cycle = -1;

    for (let i = 0; i < n; ++i) {
        if (!visited[i]) {
            let length = 0;
            let node = i;

            while (!visited[node] && edges[node] !== -1) {
                visited[node] = true;
                length++;
                node = edges[node];
            }

            if (edges[node] === i) {
                longest_cycle = Math.max(longest_cycle, length);
            }
        }
    }

    return longest_cycle;
}
```
The algorithm iterates through each node in the graph. For each unvisited node, we start a loop that increases the path length on every iteration until the current node is visited or there is no outgoing edge from the current node. If the next node is equal to the starting node, we found a cycle, which update the longest cycle if it is greater than the current longest cycle.


# Minimum Costs Using the Train Line

- slug: minimum-costs-using-the-train-line
- difficulty: Hard

A train line going through a city has two routes, the regular route and the express route. Both routes go through the **same** `n + 1` stops labeled from `0` to `n`. Initially, you start on the regular route at stop `0`.

You are given two **1-indexed** integer arrays `regular` and `express`, both of length `n`. `regular[i]` describes the cost it takes to go from stop `i - 1` to stop `i` using the regular route, and `express[i]` describes the cost it takes to go from stop `i - 1` to stop `i` using the express route.

You are also given an integer `expressCost` which represents the cost to transfer from the regular route to the express route.

Note that:

*   There is no cost to transfer from the express route back to the regular route.
*   You pay `expressCost` **every** time you transfer from the regular route to the express route.
*   There is no extra cost to stay on the express route.

Return _a **1-indexed** array_ `costs` _of length_ `n`_, where_ `costs[i]` _is the **minimum** cost to reach stop_ `i` _from stop_ `0`.

Note that a stop can be counted as **reached** from either route.

**Example 1:**

**Input:** regular = \[1,6,9,5\], express = \[5,2,3,10\], expressCost = 8
**Output:** \[1,7,14,19\]
**Explanation:** The diagram above shows how to reach stop 4 from stop 0 with minimum cost.
- Take the regular route from stop 0 to stop 1, costing 1.
- Take the express route from stop 1 to stop 2, costing 8 + 2 = 10.
- Take the express route from stop 2 to stop 3, costing 3.
- Take the regular route from stop 3 to stop 4, costing 5.
The total cost is 1 + 10 + 3 + 5 = 19.
Note that a different route could be taken to reach the other stops with minimum cost.

**Example 2:**

**Input:** regular = \[11,5,13\], express = \[7,10,6\], expressCost = 3
**Output:** \[10,15,24\]
**Explanation:** The diagram above shows how to reach stop 3 from stop 0 with minimum cost.
- Take the express route from stop 0 to stop 1, costing 3 + 7 = 10.
- Take the regular route from stop 1 to stop 2, costing 5.
- Take the express route from stop 2 to stop 3, costing 3 + 6 = 9.
The total cost is 10 + 5 + 9 = 24.
Note that the expressCost is paid again to transfer back to the express route.

**Constraints:**

*   `n == regular.length == express.length`
*   `1 <= n <= 105`
*   `1 <= regular[i], express[i], expressCost <= 105`

## A solution in Java

```java
import java.util.Arrays;

public int[] minStopCosts(int[] regular, int[] express, int expressCost) {
    int n = regular.length;
    int[] costs = new int[n];
    int regular_sum = 0, express_sum = expressCost;

    for (int i = 0; i < n; ++i) {
        regular_sum += regular[i];
        express_sum += express[i];
        costs[i] = Math.min(regular_sum, express_sum + expressCost);
    }

    return costs;
}
```
The algorithm computes the minimum cost for reaching each stop `i` from the start `0`. We maintain two sums, `regular_sum` and `express_sum`, representing the costs spent on both routes.

1. Initialize `regular_sum` to `0` and `express_sum` to `expressCost`, because we always begin from the regular route and have to pay the cost to transfer to the express route.
2. Iterate over stops from `0` to `n-1`.
3. In each iteration, update `regular_sum` by adding the cost to move to the next stop using the regular route, and update `express_sum` by adding the cost to move to the next stop using the express route.
4. For each stop `i`, the minimum cost is the minimum of either staying on the regular route or transferring to the express route and moving to the next stop. So, `costs[i] = min(regular_sum, express_sum + expressCost)`.
5. After the loop, return the array `costs`.

The time complexity is O(n) and the space complexity is O(n).

## A solution in C++

```cpp
#include <vector>
using namespace std;

vector<int> minStopCosts(vector<int>& regular, vector<int>& express, int expressCost) {
    int n = regular.size();
    vector<int> costs(n);
    int regular_sum = 0, express_sum = expressCost;

    for (int i = 0; i < n; ++i) {
        regular_sum += regular[i];
        express_sum += express[i];
        costs[i] = min(regular_sum, express_sum + expressCost);
    }

    return costs;
}
```
The algorithm computes the minimum cost for reaching each stop `i` from the start `0`. We maintain two sums, `regular_sum` and `express_sum`, representing the costs spent on both routes.

1. Initialize `regular_sum` to `0` and `express_sum` to `expressCost`, because we always begin from the regular route and have to pay the cost to transfer to the express route.
2. Iterate over stops from `0` to `n-1`.
3. In each iteration, update `regular_sum` by adding the cost to move to the next stop using the regular route, and update `express_sum` by adding the cost to move to the next stop using the express route.
4. For each stop `i`, the minimum cost is the minimum of either staying on the regular route or transferring to the express route and moving to the next stop. So, `costs[i] = min(regular_sum, express_sum + expressCost)`.
5. After the loop, return the array `costs`.

The time complexity is O(n) and the space complexity is O(n).

## A solution in Python

```python
def min_stop_costs(regular, express, express_cost):
    n = len(regular)
    costs = [0] * n
    regular_sum, express_sum = 0, express_cost

    for i in range(n):
        regular_sum += regular[i]
        express_sum += express[i]
        costs[i] = min(regular_sum, express_sum + express_cost)

    return costs
```
The algorithm computes the minimum cost for reaching each stop `i` from the start `0`. We maintain two sums, `regular_sum` and `express_sum`, representing the costs spent on both routes.

1. Initialize `regular_sum` to `0` and `express_sum` to `expressCost`, because we always begin from the regular route and have to pay the cost to transfer to the express route.
2. Iterate over stops from `0` to `n-1`.
3. In each iteration, update `regular_sum` by adding the cost to move to the next stop using the regular route, and update `express_sum` by adding the cost to move to the next stop using the express route.
4. For each stop `i`, the minimum cost is the minimum of either staying on the regular route or transferring to the express route and moving to the next stop. So, `costs[i] = min(regular_sum, express_sum + expressCost)`.
5. After the loop, return the array `costs`.

The time complexity is O(n) and the space complexity is O(n).

## A solution in Javascript

```javascript
function minStopCosts(regular, express, expressCost) {
    const n = regular.length;
    const costs = new Array(n);
    let regular_sum = 0, express_sum = expressCost;

    for (let i = 0; i < n; ++i) {
        regular_sum += regular[i];
        express_sum += express[i];
        costs[i] = Math.min(regular_sum, express_sum + expressCost);
    }

    return costs;
}
```
The algorithm computes the minimum cost for reaching each stop `i` from the start `0`. We maintain two sums, `regular_sum` and `express_sum`, representing the costs spent on both routes.

1. Initialize `regular_sum` to `0` and `express_sum` to `expressCost`, because we always begin from the regular route and have to pay the cost to transfer to the express route.
2. Iterate over stops from `0` to `n-1`.
3. In each iteration, update `regular_sum` by adding the cost to move to the next stop using the regular route, and update `express_sum` by adding the cost to move to the next stop using the express route.
4. For each stop `i`, the minimum cost is the minimum of either staying on the regular route or transferring to the express route and moving to the next stop. So, `costs[i] = min(regular_sum, express_sum + expressCost)`.
5. After the loop, return the array `costs`.

The time complexity is O(n) and the space complexity is O(n).


# Merge Similar Items

- slug: merge-similar-items
- difficulty: Easy

You are given two 2D integer arrays, `items1` and `items2`, representing two sets of items. Each array `items` has the following properties:

*   `items[i] = [valuei, weighti]` where `valuei` represents the **value** and `weighti` represents the **weight** of the `ith` item.
*   The value of each item in `items` is **unique**.

Return _a 2D integer array_ `ret` _where_ `ret[i] = [valuei, weighti]`_,_ _with_ `weighti` _being the **sum of weights** of all items with value_ `valuei`.

**Note:** `ret` should be returned in **ascending** order by value.

**Example 1:**

**Input:** items1 = \[\[1,1\],\[4,5\],\[3,8\]\], items2 = \[\[3,1\],\[1,5\]\]
**Output:** \[\[1,6\],\[3,9\],\[4,5\]\]
**Explanation:**
The item with value = 1 occurs in items1 with weight = 1 and in items2 with weight = 5, total weight = 1 + 5 = 6.
The item with value = 3 occurs in items1 with weight = 8 and in items2 with weight = 1, total weight = 8 + 1 = 9.
The item with value = 4 occurs in items1 with weight = 5, total weight = 5.
Therefore, we return \[\[1,6\],\[3,9\],\[4,5\]\].

**Example 2:**

**Input:** items1 = \[\[1,1\],\[3,2\],\[2,3\]\], items2 = \[\[2,1\],\[3,2\],\[1,3\]\]
**Output:** \[\[1,4\],\[2,4\],\[3,4\]\]
**Explanation:**
The item with value = 1 occurs in items1 with weight = 1 and in items2 with weight = 3, total weight = 1 + 3 = 4.
The item with value = 2 occurs in items1 with weight = 3 and in items2 with weight = 1, total weight = 3 + 1 = 4.
The item with value = 3 occurs in items1 with weight = 2 and in items2 with weight = 2, total weight = 2 + 2 = 4.
Therefore, we return \[\[1,4\],\[2,4\],\[3,4\]\].

**Example 3:**

**Input:** items1 = \[\[1,3\],\[2,2\]\], items2 = \[\[7,1\],\[2,2\],\[1,4\]\]
**Output:** \[\[1,7\],\[2,4\],\[7,1\]\]
**Explanation:**
The item with value = 1 occurs in items1 with weight = 3 and in items2 with weight = 4, total weight = 3 + 4 = 7.
The item with value = 2 occurs in items1 with weight = 2 and in items2 with weight = 2, total weight = 2 + 2 = 4.
The item with value = 7 occurs in items2 with weight = 1, total weight = 1.
Therefore, we return \[\[1,7\],\[2,4\],\[7,1\]\].

**Constraints:**

*   `1 <= items1.length, items2.length <= 1000`
*   `items1[i].length == items2[i].length == 2`
*   `1 <= valuei, weighti <= 1000`
*   Each `valuei` in `items1` is **unique**.
*   Each `valuei` in `items2` is **unique**.

## A solution in Java

```java
import java.util.*;

class Main {
    public static List<List<Integer>> sumWeights(List<List<Integer>> items1, List<List<Integer>> items2) {
        Map<Integer, Integer> weightSums = new TreeMap<>();
        List<List<Integer>> result = new ArrayList<>();

        for (List<Integer> item : items1) {
            weightSums.put(item.get(0), weightSums.getOrDefault(item.get(0), 0) + item.get(1));
        }
        for (List<Integer> item : items2) {
            weightSums.put(item.get(0), weightSums.getOrDefault(item.get(0), 0) + item.get(1));
        }

        for (Map.Entry<Integer, Integer> entry : weightSums.entrySet()) {
            result.add(Arrays.asList(entry.getKey(), entry.getValue()));
        }

        return result;
    }
}
```
1. Create a map called `weightSums` to store the sum of weights for every `value`. Initialize an empty list `result` for returning the solution.
2. Iterate over the `items1` and `items2` arrays. For each `[value, weight]` pair, add the weight to the `weightSums` map for that value. If the value is not present in the map, set its value to be equal to the current weight.
3. Loop through the sorted `weightSums` items (by key/values in ascending order), and append each `[value, weight]` to the `result` list.
4. Return the `result` list.

## A solution in C++

```cpp
#include <vector>
#include <map>
using namespace std;

vector<vector<int>> sumWeights(vector<vector<int>>& items1, vector<vector<int>>& items2) {
    map<int, int> weightSums;
    vector<vector<int>> result;

    for (const auto& item : items1) {
        weightSums[item[0]] += item[1];
    }
    for (const auto& item : items2) {
        weightSums[item[0]] += item[1];
    }

    for (const auto& entry : weightSums) {
        result.push_back({entry.first, entry.second});
    }

    return result;
}
```
1. Create a map called `weightSums` to store the sum of weights for every `value`. Initialize an empty list `result` for returning the solution.
2. Iterate over the `items1` and `items2` arrays. For each `[value, weight]` pair, add the weight to the `weightSums` map for that value. If the value is not present in the map, set its value to be equal to the current weight.
3. Loop through the sorted `weightSums` items (by key/values in ascending order), and append each `[value, weight]` to the `result` list.
4. Return the `result` list.

## A solution in Python

```python
def sum_weights(items1, items2):
    weight_sums = {}
    result = []

    for value, weight in items1:
        weight_sums[value] = weight_sums.get(value, 0) + weight
    for value, weight in items2:
        weight_sums[value] = weight_sums.get(value, 0) + weight

    for value, weight in sorted(weight_sums.items()):
        result.append([value, weight])

    return result
```
1. Create a map called `weightSums` to store the sum of weights for every `value`. Initialize an empty list `result` for returning the solution.
2. Iterate over the `items1` and `items2` arrays. For each `[value, weight]` pair, add the weight to the `weightSums` map for that value. If the value is not present in the map, set its value to be equal to the current weight.
3. Loop through the sorted `weightSums` items (by key/values in ascending order), and append each `[value, weight]` to the `result` list.
4. Return the `result` list.

## A solution in Javascript

```javascript
function sumWeights(items1, items2) {
    const weightSums = new Map();
    const result = [];

    items1.forEach(([value, weight]) => {
        weightSums.set(value, (weightSums.get(value) || 0) + weight);
    });
    items2.forEach(([value, weight]) => {
        weightSums.set(value, (weightSums.get(value) || 0) + weight);
    });

    for (const [value, weight] of Array.from(weightSums.entries()).sort((a, b) => a[0] - b[0])) {
        result.push([value, weight]);
    }

    return result;
}
```
1. Create a map called `weightSums` to store the sum of weights for every `value`. Initialize an empty list `result` for returning the solution.
2. Iterate over the `items1` and `items2` arrays. For each `[value, weight]` pair, add the weight to the `weightSums` map for that value. If the value is not present in the map, set its value to be equal to the current weight.
3. Loop through the sorted `weightSums` items (by key/values in ascending order), and append each `[value, weight]` to the `result` list.
4. Return the `result` list.


# Count Number of Bad Pairs

- slug: count-number-of-bad-pairs
- difficulty: Medium

You are given a **0-indexed** integer array `nums`. A pair of indices `(i, j)` is a **bad pair** if `i < j` and `j - i != nums[j] - nums[i]`.

Return _the total number of **bad pairs** in_ `nums`.

**Example 1:**

**Input:** nums = \[4,1,3,3\]
**Output:** 5
**Explanation:** The pair (0, 1) is a bad pair since 1 - 0 != 1 - 4.
The pair (0, 2) is a bad pair since 2 - 0 != 3 - 4, 2 != -1.
The pair (0, 3) is a bad pair since 3 - 0 != 3 - 4, 3 != -1.
The pair (1, 2) is a bad pair since 2 - 1 != 3 - 1, 1 != 2.
The pair (2, 3) is a bad pair since 3 - 2 != 3 - 3, 1 != 0.
There are a total of 5 bad pairs, so we return 5.

**Example 2:**

**Input:** nums = \[1,2,3,4,5\]
**Output:** 0
**Explanation:** There are no bad pairs.

**Constraints:**

*   `1 <= nums.length <= 105`
*   `1 <= nums[i] <= 109`

## A solution in Java

```java
public int badPairs(int[] nums) {
    int count = 0;
    for (int i = 0; i < nums.length; i++) {
        for (int j = i + 1; j < nums.length; j++) {
            if (j - i != nums[j] - nums[i]) {
                count++;
            }
        }
    }
    return count;
}
```
The algorithm iterates through all possible pairs of indices (i, j) in the given array `nums`, where i < j. For each pair, it checks if the condition `(j - i) != (nums[j] - nums[i])` is true. If it is, it increments the counter `count`. The algorithm has a nested loop structure and its time complexity is O(n^2). Finally, it returns the total count of bad pairs found.

## A solution in C++

```cpp
int badPairs(vector<int>& nums) {
    int count = 0;
    for (int i = 0; i < nums.size(); i++) {
        for (int j = i + 1; j < nums.size(); j++) {
            if (j - i != nums[j] - nums[i]) {
                count++;
            }
        }
    }
    return count;
}
```
The algorithm iterates through all possible pairs of indices (i, j) in the given array `nums`, where i < j. For each pair, it checks if the condition `(j - i) != (nums[j] - nums[i])` is true. If it is, it increments the counter `count`. The algorithm has a nested loop structure and its time complexity is O(n^2). Finally, it returns the total count of bad pairs found.

## A solution in Python

```python
def bad_pairs(nums):
    count = 0
    for i in range(len(nums)):
        for j in range(i + 1, len(nums)):
            if j - i != nums[j] - nums[i]:
                count += 1
    return count
```
The algorithm iterates through all possible pairs of indices (i, j) in the given array `nums`, where i < j. For each pair, it checks if the condition `(j - i) != (nums[j] - nums[i])` is true. If it is, it increments the counter `count`. The algorithm has a nested loop structure and its time complexity is O(n^2). Finally, it returns the total count of bad pairs found.

## A solution in Javascript

```javascript
function badPairs(nums) {
    let count = 0;
    for (let i = 0; i < nums.length; i++) {
        for (let j = i + 1; j < nums.length; j++) {
            if (j - i !== nums[j] - nums[i]) {
                count++;
            }
        }
    }
    return count;
}
```
The algorithm iterates through all possible pairs of indices (i, j) in the given array `nums`, where i < j. For each pair, it checks if the condition `(j - i) != (nums[j] - nums[i])` is true. If it is, it increments the counter `count`. The algorithm has a nested loop structure and its time complexity is O(n^2). Finally, it returns the total count of bad pairs found.


# Task Scheduler II

- slug: task-scheduler-ii
- difficulty: Medium

You are given a **0-indexed** array of positive integers `tasks`, representing tasks that need to be completed **in order**, where `tasks[i]` represents the **type** of the `ith` task.

You are also given a positive integer `space`, which represents the **minimum** number of days that must pass **after** the completion of a task before another task of the **same** type can be performed.

Each day, until all tasks have been completed, you must either:

*   Complete the next task from `tasks`, or
*   Take a break.

Return _the **minimum** number of days needed to complete all tasks_.

**Example 1:**

**Input:** tasks = \[1,2,1,2,3,1\], space = 3
**Output:** 9
**Explanation:**
One way to complete all tasks in 9 days is as follows:
Day 1: Complete the 0th task.
Day 2: Complete the 1st task.
Day 3: Take a break.
Day 4: Take a break.
Day 5: Complete the 2nd task.
Day 6: Complete the 3rd task.
Day 7: Take a break.
Day 8: Complete the 4th task.
Day 9: Complete the 5th task.
It can be shown that the tasks cannot be completed in less than 9 days.

**Example 2:**

**Input:** tasks = \[5,8,8,5\], space = 2
**Output:** 6
**Explanation:**
One way to complete all tasks in 6 days is as follows:
Day 1: Complete the 0th task.
Day 2: Complete the 1st task.
Day 3: Take a break.
Day 4: Take a break.
Day 5: Complete the 2nd task.
Day 6: Complete the 3rd task.
It can be shown that the tasks cannot be completed in less than 6 days.

**Constraints:**

*   `1 <= tasks.length <= 105`
*   `1 <= tasks[i] <= 109`
*   `1 <= space <= tasks.length`

## A solution in Java

```java
public int minDaysToCompleteTasks(int[] tasks, int space) {
    HashMap<Integer, Integer> taskLastComplete = new HashMap<>();
    int days = 0;

    for (int task : tasks) {
        if (taskLastComplete.containsKey(task)) {
            int passedDays = days - taskLastComplete.get(task);
            if (passedDays < space) {
                days += space - passedDays;
            }
        }
        days++;
        taskLastComplete.put(task, days);
    }

    return days;
}
```
The algorithm uses a hash table to keep track of the last completed day for each task type. It iterates through the given `tasks`, and for each task, it calculates the number of days passed since the last completion of the same task type. If the passed days are less than the required `space`, we add the difference to the current `days` count, simulating taking breaks until the task can be performed again. Finally, we increment the days count, as well as updating the last completed day of the task type in the hash table. The algorithm returns the total number of days needed to complete all tasks.

## A solution in C++

```cpp
int min_days_to_complete_tasks(vector<int>& tasks, int space) {
    unordered_map<int, int> task_last_complete;
    int days = 0;

    for (int task : tasks) {
        if (task_last_complete.find(task) != task_last_complete.end()) {
            int passed_days = days - task_last_complete[task];
            if (passed_days < space) {
                days += space - passed_days;
            }
        }
        task_last_complete[task] = ++days;
    }

    return days;
}
```
The algorithm uses a hash table to keep track of the last completed day for each task type. It iterates through the given `tasks`, and for each task, it calculates the number of days passed since the last completion of the same task type. If the passed days are less than the required `space`, we add the difference to the current `days` count, simulating taking breaks until the task can be performed again. Finally, we increment the days count, as well as updating the last completed day of the task type in the hash table. The algorithm returns the total number of days needed to complete all tasks.

## A solution in Python

```python
def min_days_to_complete_tasks(tasks, space):
    task_last_complete = {}
    days = 0

    for task in tasks:
        if task in task_last_complete:
            passed_days = days - task_last_complete[task]
            if passed_days < space:
                days += space - passed_days
        days += 1
        task_last_complete[task] = days

    return days
```
The algorithm uses a hash table to keep track of the last completed day for each task type. It iterates through the given `tasks`, and for each task, it calculates the number of days passed since the last completion of the same task type. If the passed days are less than the required `space`, we add the difference to the current `days` count, simulating taking breaks until the task can be performed again. Finally, we increment the days count, as well as updating the last completed day of the task type in the hash table. The algorithm returns the total number of days needed to complete all tasks.

## A solution in Javascript

```javascript
function minDaysToCompleteTasks(tasks, space) {
    const taskLastComplete = {};
    let days = 0;

    for (const task of tasks) {
        if (task in taskLastComplete) {
            const passedDays = days - taskLastComplete[task];
            if (passedDays < space) {
                days += space - passedDays;
            }
        }
        days++;
        taskLastComplete[task] = days;
    }

    return days;
}
```
The algorithm uses a hash table to keep track of the last completed day for each task type. It iterates through the given `tasks`, and for each task, it calculates the number of days passed since the last completion of the same task type. If the passed days are less than the required `space`, we add the difference to the current `days` count, simulating taking breaks until the task can be performed again. Finally, we increment the days count, as well as updating the last completed day of the task type in the hash table. The algorithm returns the total number of days needed to complete all tasks.


# Minimum Replacements to Sort the Array

- slug: minimum-replacements-to-sort-the-array
- difficulty: Hard

You are given a **0-indexed** integer array `nums`. In one operation you can replace any element of the array with **any two** elements that **sum** to it.

*   For example, consider `nums = [5,6,7]`. In one operation, we can replace `nums[1]` with `2` and `4` and convert `nums` to `[5,2,4,7]`.

Return _the minimum number of operations to make an array that is sorted in **non-decreasing** order_.

**Example 1:**

**Input:** nums = \[3,9,3\]
**Output:** 2
**Explanation:** Here are the steps to sort the array in non-decreasing order:
- From \[3,9,3\], replace the 9 with 3 and 6 so the array becomes \[3,3,6,3\]
- From \[3,3,6,3\], replace the 6 with 3 and 3 so the array becomes \[3,3,3,3,3\]
There are 2 steps to sort the array in non-decreasing order. Therefore, we return 2.

**Example 2:**

**Input:** nums = \[1,2,3,4,5\]
**Output:** 0
**Explanation:** The array is already in non-decreasing order. Therefore, we return 0.

**Constraints:**

*   `1 <= nums.length <= 105`
*   `1 <= nums[i] <= 109`

## A solution in Java

```java
public int minOperations(int[] nums) {
    int operations = 0;
    for (int i = 1; i < nums.length; i++) {
        if (nums[i] <= nums[i - 1]) {
            int diff = nums[i - 1] - nums[i] + 1;
            operations += Math.ceil(Math.log(diff) / Math.log(2));
            nums[i] += diff;
        }
    }
    return operations;
}
```
We iterate through the array, starting from the second element. If the current element is smaller than or equal to the previous element, we calculate the difference between them and add 1. We then calculate the number of division by 2 operations needed to reach that difference using a logarithm with base 2, and round that number up to the nearest integer (as we're only interested in whole operations). We add the difference to the current element to make sure it's greater than the previous element, and increment the number of operations. We continue this process for the rest of the array, and finally return the total number of operations needed to sort the array in non-decreasing order.

## A solution in C++

```cpp
int minOperations(vector<int>& nums) {
    int operations = 0;
    for (int i = 1; i < nums.size(); i++) {
        if (nums[i] <= nums[i - 1]) {
            int diff = nums[i - 1] - nums[i] + 1;
            operations += ceil(log2(diff));
            nums[i] += diff;
        }
    }
    return operations;
}
```
We iterate through the array, starting from the second element. If the current element is smaller than or equal to the previous element, we calculate the difference between them and add 1. We then calculate the number of division by 2 operations needed to reach that difference using a logarithm with base 2, and round that number up to the nearest integer (as we're only interested in whole operations). We add the difference to the current element to make sure it's greater than the previous element, and increment the number of operations. We continue this process for the rest of the array, and finally return the total number of operations needed to sort the array in non-decreasing order.

## A solution in Python

```python
def min_operations(nums):
    operations = 0
    for i in range(1, len(nums)):
        if nums[i] <= nums[i - 1]:
            diff = nums[i - 1] - nums[i] + 1
            operations += math.ceil(math.log2(diff))
            nums[i] += diff
    return operations
```
We iterate through the array, starting from the second element. If the current element is smaller than or equal to the previous element, we calculate the difference between them and add 1. We then calculate the number of division by 2 operations needed to reach that difference using a logarithm with base 2, and round that number up to the nearest integer (as we're only interested in whole operations). We add the difference to the current element to make sure it's greater than the previous element, and increment the number of operations. We continue this process for the rest of the array, and finally return the total number of operations needed to sort the array in non-decreasing order.

## A solution in Javascript

```javascript
function minOperations(nums) {
    let operations = 0;
    for (let i = 1; i < nums.length; i++) {
        if (nums[i] <= nums[i - 1]) {
            let diff = nums[i - 1] - nums[i] + 1;
            operations += Math.ceil(Math.log2(diff));
            nums[i] += diff;
        }
    }
    return operations;
}
```
We iterate through the array, starting from the second element. If the current element is smaller than or equal to the previous element, we calculate the difference between them and add 1. We then calculate the number of division by 2 operations needed to reach that difference using a logarithm with base 2, and round that number up to the nearest integer (as we're only interested in whole operations). We add the difference to the current element to make sure it's greater than the previous element, and increment the number of operations. We continue this process for the rest of the array, and finally return the total number of operations needed to sort the array in non-decreasing order.


# Number of Arithmetic Triplets

- slug: number-of-arithmetic-triplets
- difficulty: Easy

You are given a **0-indexed**, **strictly increasing** integer array `nums` and a positive integer `diff`. A triplet `(i, j, k)` is an **arithmetic triplet** if the following conditions are met:

*   `i < j < k`,
*   `nums[j] - nums[i] == diff`, and
*   `nums[k] - nums[j] == diff`.

Return _the number of unique **arithmetic triplets**._

**Example 1:**

**Input:** nums = \[0,1,4,6,7,10\], diff = 3
**Output:** 2
**Explanation:**
(1, 2, 4) is an arithmetic triplet because both 7 - 4 == 3 and 4 - 1 == 3.
(2, 4, 5) is an arithmetic triplet because both 10 - 7 == 3 and 7 - 4 == 3.

**Example 2:**

**Input:** nums = \[4,5,6,7,8,9\], diff = 2
**Output:** 2
**Explanation:**
(0, 2, 4) is an arithmetic triplet because both 8 - 6 == 2 and 6 - 4 == 2.
(1, 3, 5) is an arithmetic triplet because both 9 - 7 == 2 and 7 - 5 == 2.

**Constraints:**

*   `3 <= nums.length <= 200`
*   `0 <= nums[i] <= 200`
*   `1 <= diff <= 50`
*   `nums` is **strictly** increasing.

## A solution in Java

```java
public int countArithmeticTriplets(int[] nums, int diff) {
    int count = 0;
    HashMap<Integer, Integer> mp = new HashMap<>();
    for (int num : nums) {
        if (mp.containsKey(num - diff))
            count += mp.get(num - diff);
        mp.put(num, mp.getOrDefault(num, 0) + 1);
    }
    return count;
}
```
The algorithm uses a hashmap to store the number of occurrences of the elements in the given array. For each element, we check if the hashmap contains the element minus the given difference (i.e., `num - diff`). If it does, that means we have found a valid arithmetic triplet, so we increment the count by the number of occurrences of that element minus the difference. Finally, we update the number of occurrences of the current element in the hashmap.

This algorithm has a time complexity of O(n), where n is the size of the given array, since we iterate through the array once and perform lookups, insertions, and updates on the hashmap in constant time.

## A solution in C++

```cpp
int countArithmeticTriplets(vector<int> &nums, int diff) {
    int count = 0;
    unordered_map<int, int> mp;
    for (int num : nums) {
        if (mp.count(num - diff))
            count += mp[num - diff];
        mp[num]++;
    }
    return count;
}
```
The algorithm uses a hashmap to store the number of occurrences of the elements in the given array. For each element, we check if the hashmap contains the element minus the given difference (i.e., `num - diff`). If it does, that means we have found a valid arithmetic triplet, so we increment the count by the number of occurrences of that element minus the difference. Finally, we update the number of occurrences of the current element in the hashmap.

This algorithm has a time complexity of O(n), where n is the size of the given array, since we iterate through the array once and perform lookups, insertions, and updates on the hashmap in constant time.

## A solution in Python

```python
def count_arithmetic_triplets(nums, diff):
    count = 0
    mp = {}
    for num in nums:
        if num - diff in mp:
            count += mp[num - diff]
        mp[num] = mp.get(num, 0) + 1
    return count
```
The algorithm uses a hashmap to store the number of occurrences of the elements in the given array. For each element, we check if the hashmap contains the element minus the given difference (i.e., `num - diff`). If it does, that means we have found a valid arithmetic triplet, so we increment the count by the number of occurrences of that element minus the difference. Finally, we update the number of occurrences of the current element in the hashmap.

This algorithm has a time complexity of O(n), where n is the size of the given array, since we iterate through the array once and perform lookups, insertions, and updates on the hashmap in constant time.

## A solution in Javascript

```javascript
function countArithmeticTriplets(nums, diff) {
    let count = 0;
    const mp = new Map();
    for (const num of nums) {
        if (mp.has(num - diff))
            count += mp.get(num - diff);
        mp.set(num, (mp.get(num) || 0) + 1);
    }
    return count;
}
```
The algorithm uses a hashmap to store the number of occurrences of the elements in the given array. For each element, we check if the hashmap contains the element minus the given difference (i.e., `num - diff`). If it does, that means we have found a valid arithmetic triplet, so we increment the count by the number of occurrences of that element minus the difference. Finally, we update the number of occurrences of the current element in the hashmap.

This algorithm has a time complexity of O(n), where n is the size of the given array, since we iterate through the array once and perform lookups, insertions, and updates on the hashmap in constant time.


# Reachable Nodes With Restrictions

- slug: reachable-nodes-with-restrictions
- difficulty: Medium

There is an undirected tree with `n` nodes labeled from `0` to `n - 1` and `n - 1` edges.

You are given a 2D integer array `edges` of length `n - 1` where `edges[i] = [ai, bi]` indicates that there is an edge between nodes `ai` and `bi` in the tree. You are also given an integer array `restricted` which represents **restricted** nodes.

Return _the **maximum** number of nodes you can reach from node_ `0` _without visiting a restricted node._

Note that node `0` will **not** be a restricted node.

**Example 1:**

**Input:** n = 7, edges = \[\[0,1\],\[1,2\],\[3,1\],\[4,0\],\[0,5\],\[5,6\]\], restricted = \[4,5\]
**Output:** 4
**Explanation:** The diagram above shows the tree.
We have that \[0,1,2,3\] are the only nodes that can be reached from node 0 without visiting a restricted node.

**Example 2:**

**Input:** n = 7, edges = \[\[0,1\],\[0,2\],\[0,5\],\[0,4\],\[3,2\],\[6,5\]\], restricted = \[4,2,1\]
**Output:** 3
**Explanation:** The diagram above shows the tree.
We have that \[0,5,6\] are the only nodes that can be reached from node 0 without visiting a restricted node.

**Constraints:**

*   `2 <= n <= 105`
*   `edges.length == n - 1`
*   `edges[i].length == 2`
*   `0 <= ai, bi < n`
*   `ai != bi`
*   `edges` represents a valid tree.
*   `1 <= restricted.length < n`
*   `1 <= restricted[i] < n`
*   All the values of `restricted` are **unique**.

## A solution in Java

```java
import java.util.*;

public int maxNodesWithoutRestricted(int n, int[][] edges, int[] restricted) {
    Map<Integer, List<Integer>> tree = new HashMap<>();
    Set<Integer> blockedNodes = new HashSet<>();
    for (int r : restricted) blockedNodes.add(r);

    for (int[] edge : edges) {
        tree.putIfAbsent(edge[0], new ArrayList<>());
        tree.putIfAbsent(edge[1], new ArrayList<>());
        tree.get(edge[0]).add(edge[1]);
        tree.get(edge[1]).add(edge[0]);
    }

    int[] maxNodes = new int[1];

    dfs(0, 1, -1, tree, blockedNodes, maxNodes);
    return maxNodes[0];
}

private void dfs(int node, int depth, int parent, Map<Integer, List<Integer>> tree, Set<Integer> blockedNodes, int[] maxNodes) {
    if (blockedNodes.contains(node)) return;
    maxNodes[0] = Math.max(maxNodes[0], depth);
    for (int child : tree.get(node)) {
        if (child != parent) dfs(child, depth + 1, node, tree, blockedNodes, maxNodes);
    }
}
```
1. Create an adjacency list (i.e., a data structure representing the tree) from the edges.
2. Convert the list of restricted nodes to a set to efficiently check if a node is restricted.
3. Use depth-first search (DFS) to traverse the tree from the starting node (0) while avoiding restricted nodes.
   - If a node is restricted, immediately return and stop traversing that branch.
   - Keep track of the maximum depth reached.
4. Return the maximum depth found in the DFS traversal as the answer.

## A solution in C++

```cpp
#include <set>
#include <unordered_map>
#include <vector>

int maxNodesWithoutRestricted(int n, const std::vector<std::vector<int>>& edges, const std::vector<int>& restricted) {
    std::unordered_map<int, std::vector<int>> tree;
    std::set<int> blocked_nodes(restricted.begin(), restricted.end());

    for (const auto& edge : edges) {
        tree[edge[0]].push_back(edge[1]);
        tree[edge[1]].push_back(edge[0]);
    }

    int max_nodes = 0;
    std::function<void(int, int, int)> dfs = [&](int node, int depth, int parent) {
        if (blocked_nodes.count(node)) return;
        max_nodes = std::max(max_nodes, depth);
        for (int child : tree[node]) {
            if (child != parent) dfs(child, depth + 1, node);
        }
    };

    dfs(0, 1, -1);
    return max_nodes;
}
```
1. Create an adjacency list (i.e., a data structure representing the tree) from the edges.
2. Convert the list of restricted nodes to a set to efficiently check if a node is restricted.
3. Use depth-first search (DFS) to traverse the tree from the starting node (0) while avoiding restricted nodes.
   - If a node is restricted, immediately return and stop traversing that branch.
   - Keep track of the maximum depth reached.
4. Return the maximum depth found in the DFS traversal as the answer.

## A solution in Python

```python
from collections import defaultdict

def max_nodes_without_restricted(n, edges, restricted):
    tree = defaultdict(list)
    blocked_nodes = set(restricted)

    for a, b in edges:
        tree[a].append(b)
        tree[b].append(a)

    max_nodes = [0]

    def dfs(node, depth, parent):
        if node in blocked_nodes:
            return
        max_nodes[0] = max(max_nodes[0], depth)
        for child in tree[node]:
            if child != parent:
                dfs(child, depth + 1, node)

    dfs(0, 1, -1)
    return max_nodes[0]
```
1. Create an adjacency list (i.e., a data structure representing the tree) from the edges.
2. Convert the list of restricted nodes to a set to efficiently check if a node is restricted.
3. Use depth-first search (DFS) to traverse the tree from the starting node (0) while avoiding restricted nodes.
   - If a node is restricted, immediately return and stop traversing that branch.
   - Keep track of the maximum depth reached.
4. Return the maximum depth found in the DFS traversal as the answer.

## A solution in Javascript

```javascript
function maxNodesWithoutRestricted(n, edges, restricted) {
    const tree = new Map();
    const blockedNodes = new Set(restricted);

    for (const [a, b] of edges) {
        if (!tree.has(a)) tree.set(a, []);
        if (!tree.has(b)) tree.set(b, []);
        tree.get(a).push(b);
        tree.get(b).push(a);
    }

    let maxNodes = 0;

    function dfs(node, depth, parent) {
        if (blockedNodes.has(node)) return;
        maxNodes = Math.max(maxNodes, depth);
        for (const child of tree.get(node)) {
            if (child !== parent) dfs(child, depth + 1, node);
        }
    }

    dfs(0, 1, -1);
    return maxNodes;
}
```
1. Create an adjacency list (i.e., a data structure representing the tree) from the edges.
2. Convert the list of restricted nodes to a set to efficiently check if a node is restricted.
3. Use depth-first search (DFS) to traverse the tree from the starting node (0) while avoiding restricted nodes.
   - If a node is restricted, immediately return and stop traversing that branch.
   - Keep track of the maximum depth reached.
4. Return the maximum depth found in the DFS traversal as the answer.