As the ruler of a kingdom, you have an army of wizards at your command.
You are given a 0-indexed integer array strength
, where strength[i]
denotes the strength of the i<sup>th</sup>
wizard. For a contiguous group of wizards (i.e. the wizards' strengths form a subarray of strength
), the total strength is defined as the product of the following two values:
Return the sum of the total strengths of all contiguous groups of wizards. Since the answer may be very large, return it modulo 10<sup>9</sup> + 7
.
A subarray is a contiguous non-empty sequence of elements within an array.
Example 1:
Input: strength = [1,3,1,2]
Output: 44
Example 2:
Input: strength = [5,4,6]
Output: 213
Constraints:
1 <= strength.length <= 10^5
1 <= strength[i] <= 10^9
#include <iostream>
#include <vector>
using namespace std;
const int MOD = 1e9 + 7;
// Naive Solution: O(n^2)
long long totalStrengthNaive(vector<int>& strength) {
int n = strength.size();
long long total_strength = 0;
for (int i = 0; i < n; ++i) {
for (int j = i; j < n; ++j) {
int min_strength = strength[i];
long long sum = 0;
for (int k = i; k <= j; ++k) {
min_strength = min(min_strength, strength[k]);
sum = (sum + strength[k]) % MOD;
}
total_strength = (total_strength + (long long)min_strength * sum) % MOD;
}
}
return total_strength;
}
// Optimized Solution: O(n)
long long totalStrengthOptimized(vector<int>& strength) {
int n = strength.size();
vector<long long> left(n), right(n);
// Find left boundary: the distance to the first element smaller than strength[i] on the left
vector<int> stack;
for (int i = 0; i < n; ++i) {
while (!stack.empty() && strength[stack.back()] > strength[i]) {
stack.pop_back();
}
left[i] = stack.empty() ? i + 1 : i - stack.back();
stack.push_back(i);
}
stack.clear();
// Find right boundary: the distance to the first element smaller or equal to strength[i] on the right
for (int i = n - 1; i >= 0; --i) {
while (!stack.empty() && strength[stack.back()] >= strength[i]) {
stack.pop_back();
}
right[i] = stack.empty() ? n - i : stack.back() - i;
stack.push_back(i);
}
// Calculate prefix sum and prefix sum of prefix sum
vector<long long> prefix_sum(n + 1, 0), prefix_sum_prefix_sum(n + 1, 0);
for (int i = 0; i < n; ++i) {
prefix_sum[i + 1] = (prefix_sum[i] + strength[i]) % MOD;
prefix_sum_prefix_sum[i + 1] = (prefix_sum_prefix_sum[i] + prefix_sum[i + 1]) % MOD;
}
long long total_strength = 0;
for (int i = 0; i < n; ++i) {
long long left_sum = (prefix_sum_prefix_sum[i + 1] - prefix_sum_prefix_sum[max(0, i - (int)left[i] + 1)] - (long long)(i - left[i] + 1) * (prefix_sum[max(0, i - (int)left[i] + 1)])) % MOD;
long long right_sum = ((long long)(i + right[i] + 1) * (prefix_sum[min(n, i + (int)right[i] + 1)]) - prefix_sum_prefix_sum[min(n, i + (int)right[i] + 1)] - (prefix_sum_prefix_sum[i + 1])) % MOD;
total_strength = (total_strength + strength[i] * (left_sum * right[i] % MOD + right_sum * left[i] % MOD) % MOD) % MOD;
}
return (total_strength + MOD) % MOD; // Ensure result is non-negative
}
int main() {
vector<int> strength1 = {1, 3, 1, 2};
cout << "Total strength (naive) for {1, 3, 1, 2}: " << totalStrengthNaive(strength1) << endl;
cout << "Total strength (optimized) for {1, 3, 1, 2}: " << totalStrengthOptimized(strength1) << endl;
vector<int> strength2 = {5, 4, 6};
cout << "Total strength (naive) for {5, 4, 6}: " << totalStrengthNaive(strength2) << endl;
cout << "Total strength (optimized) for {5, 4, 6}: " << totalStrengthOptimized(strength2) << endl;
return 0;
}
The naive solution iterates through all possible subarrays, calculates the minimum strength within each subarray, calculates the sum of strengths within each subarray, and then multiplies these two values together. Finally, it sums up all these products modulo 10^9 + 7
. This approach is straightforward but inefficient.
The optimized solution utilizes a stack to find the left and right boundaries for each element, indicating the range where that element is the minimum. It then uses prefix sums and prefix sums of prefix sums to efficiently calculate the sum of strengths within these ranges. This significantly reduces the time complexity.
// Optimized Solution: O(n)
long long totalStrengthOptimized(vector<int>& strength) {
int n = strength.size();
vector<long long> left(n), right(n);
// Find left boundary: the distance to the first element smaller than strength[i] on the left
vector<int> stack;
for (int i = 0; i < n; ++i) {
while (!stack.empty() && strength[stack.back()] > strength[i]) {
stack.pop_back();
}
left[i] = stack.empty() ? i + 1 : i - stack.back();
stack.push_back(i);
}
stack.clear();
// Find right boundary: the distance to the first element smaller or equal to strength[i] on the right
for (int i = n - 1; i >= 0; --i) {
while (!stack.empty() && strength[stack.back()] >= strength[i]) {
stack.pop_back();
}
right[i] = stack.empty() ? n - i : stack.back() - i;
stack.push_back(i);
}
// Calculate prefix sum and prefix sum of prefix sum
vector<long long> prefix_sum(n + 1, 0), prefix_sum_prefix_sum(n + 1, 0);
for (int i = 0; i < n; ++i) {
prefix_sum[i + 1] = (prefix_sum[i] + strength[i]) % MOD;
prefix_sum_prefix_sum[i + 1] = (prefix_sum_prefix_sum[i] + prefix_sum[i + 1]) % MOD;
}
long long total_strength = 0;
for (int i = 0; i < n; ++i) {
long long left_sum = (prefix_sum_prefix_sum[i + 1] - prefix_sum_prefix_sum[max(0, i - (int)left[i] + 1)] - (long long)(i - left[i] + 1) * (prefix_sum[max(0, i - (int)left[i] + 1)])) % MOD;
long long right_sum = ((long long)(i + right[i] + 1) * (prefix_sum[min(n, i + (int)right[i] + 1)]) - prefix_sum_prefix_sum[min(n, i + (int)right[i] + 1)] - (prefix_sum_prefix_sum[i + 1])) % MOD;
total_strength = (total_strength + strength[i] * (left_sum * right[i] % MOD + right_sum * left[i] % MOD) % MOD) % MOD;
}
return (total_strength + MOD) % MOD; // Ensure result is non-negative
}
n
(left, right, prefix_sum, prefix_sum_prefix_sum) resulting in a space complexity of O(n).1 <= strength[i] <= 10^9
, it's crucial to use long long
to prevent integer overflow when calculating sums and products. The provided code uses long long
for these calculations.(10^9 + 7)
after each multiplication and addition to prevent overflow and ensure the result remains within the specified range. The provided code correctly uses the modulo operator.