Hamada14's blog

By Hamada14, history, 8 years ago, In English

I have been trying to solve a problem where i needed to find the sum of the minimum of all the subarrays of an array. If someone know of an O(N) algorithm to such problem. Thanks.

  • Vote: I like it
  • +7
  • Vote: I do not like it

| Write comment?
»
8 years ago, # |
  Vote: I like it +7 Vote: I do not like it
»
8 years ago, # |
  Vote: I like it +3 Vote: I do not like it

You can see a similar problem Here . Check out it's editorial for solution details.

»
8 years ago, # |
  Vote: I like it 0 Vote: I do not like it

The first idea that comes to mind is an O(NlogN) algorithm to solve it, which is worse than O(N) but by a small margin.

Let x be the smallest element in the array and i be its index in the array (starting from 1). It's not hard to see that the amount of subarrays that contain x is equal to i*(N+1-i), where N is the index of the last element, and obviously the minimum of all these subarrays is x, so we can add x*i*(N+1-i) to the answer. Now, for every other y in the array, y cannot be the minimum for any subarray that contains x. So, we can split our array in two arrays [1,i-1] and [i+1,N] and solve the problem recursively, summing up the partial results in the end. Don't forget to fix the indexes at every step of the algorithm (the values for i and N will change depending on the current sub-array) At each step we have to find the minimum (this step will take O(logN) if you use a segment tree) and we have exactly N steps because at each step we remove one element from the array, so the final complexity is O(NlogN). If you choose to use sparse table instead of segment tree to perform the min queries, you can get min queries in O(1) but the cost to build the table is O(NlogN) so in the end of the day its all the same.

If you are confused i coded this: http://code.geeksforgeeks.org/paP9K7

»
8 years ago, # |
Rev. 3   Vote: I like it +20 Vote: I do not like it

Yes, it can be done in O(n).
For each position i, find the nearest element to its right whose value is less than that at i (if such element does not exist, consider this parameter to be n+1, where n is size of array). This whole process can be done in O(n) using stack.
Now start processing the array from right to left. Let DP[i] denote the sum of minimum of subarrays [i, j] such that i <= j <= n. Assume you are currently processing position i and the value of the above parameter is p. For all subarrays [i, j] such that i <= j < p, the minimum will be arr[i]. And for all subarrays [i, j] such that p <= j <= n, the sum of minimum of these subarrays has already been computed and stored in DP[p].
So, your formula becomes: DP[i] = (p-i)*arr[i] + DP[p]

UPD: This is the exact same problem, from a recent contest.
My code for the same can be found here.

»
8 years ago, # |
  Vote: I like it +13 Vote: I do not like it

I solved this problem using DSU, which isn't exactly O(N) but it's very efficient nevertheless. If we add elements in reverse order (from largest to smallest), we can keep blocks of already added contiguous elements using DSU. If current block goes from l to r and we just added Ai, then we must add to the answer (i - l + 1) * (r - i + 1) * Ai (all sub-blocks having Ai as minimum).

  • »
    »
    8 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    Didn't understand. Code snippet please!

    • »
      »
      »
      8 years ago, # ^ |
        Vote: I like it 0 Vote: I do not like it
      Code
    • »
      »
      »
      8 years ago, # ^ |
      Rev. 2   Vote: I like it 0 Vote: I do not like it

      Yes, sure. Here's the code -> Sum of all subarrays minimums

      Further explanation of the solution: If current block goes from l to r, it means that Al - 1 and Ar + 1 are smaller than the current element, because we're adding elements from largest to smallest. This means any subarray containing either Al - 1 or Ar + 1 won't have Ai as minimum element, so all subarrays that will have Ai as minimum will be completely contained within the current block (and have to include Ai obviously). If subarray goes from p to q, we need p ≤ l and q ≥ r, so we have i - l + 1 possible positions for p and r - i + 1 possible positions for q, that's why we have to add (i - l + 1) * (r - i + 1) * Ai to the answer.

      • »
        »
        »
        »
        8 years ago, # ^ |
          Vote: I like it +5 Vote: I do not like it

        Thank you! Don't you think it's better to put code in spoiler style, so that code and explanation are close together? :)

»
8 years ago, # |
  Vote: I like it +5 Vote: I do not like it

I was wondering how can these problem be solved.

1) Sum of averages of all the subarrays?
2) Sum of medians of all the subarrays?

Thanks in advance!

  • »
    »
    8 years ago, # ^ |
    Rev. 9   Vote: I like it +12 Vote: I do not like it

    I can answer the first question for now.

    Average of a subarray [l, r] can be expressed as . From this, we can see that if element Ai appears at x subarrays of size s, then this will contribute to the final answer with .

    Now the problem reduces to "how many times does each element appear at some subarray of size s for every size s in the range [1, N]?. We need to calculate the matrix of occurrences and we're done. This matrix, for N = 7 for example, will look as follows...

    1 1 1 1 1 1 1
    1 2 2 2 2 2 1
    1 2 3 3 3 2 1
    1 2 3 4 3 2 1
    1 2 3 3 3 2 1
    1 2 2 2 2 2 1
    1 1 1 1 1 1 1
    

    The same will apply for bigger sizes as well. Column i will be equal to column i - 1 but with range [i, N - i + 1] increased by 1. This can be achieved in linear time with prefix sums. Let and Fi be the contribution of element Ai to the final answer (divided by Ai). Initially F1 = FN = SN, then for every i we perform the assignment Fi = Fi - 1 + SN - i + 1 - Si - 1 if i is in the first half of the array or Fi = Fi - 1 + Si - SN - i if i in the second half of the array. This last bit can be done easily with two pointers, incrementing one and decrementing the other.

    Here's the code for the solution: Sum of averages

    • »
      »
      »
      8 years ago, # ^ |
        Vote: I like it 0 Vote: I do not like it

      Looks good!! Thanks!

      Any idea about how to do second one?
      
»
8 years ago, # |
  Vote: I like it +5 Vote: I do not like it

Thanks a lot for everyone for their great and smart solutions.

To keep you updated after thinking i found this O(n) solution that uses a Stack. It's based on counting the number along with every index as long as the value in the current index is great than it's value and when the current value is below the value in the stack i start removing from the stack.


#include<bits/stdc++.h> #define mp make_pair using namespace std; int n; int arr[100005]; stack< pair<int,int> > st; int main() { cin >> n; for(int i = 0; i < n; i++) cin >> arr[i]; int curSum = 0, res = 0; for (int j = 0; j < n; j++) { if((!st.empty()) && arr[j] >= st.top().first) { curSum += arr[j]; st.push(mp(arr[j], 1)); }else { int cnt = 1; while((!st.empty()) && st.top().first > arr[j]) cnt += st.top().second, curSum -= st.top().second * st.top().first,st.pop(); st.push(mp(arr[j], cnt)); curSum += cnt * arr[j]; } res += curSum; } cout << res << endl; return 0; }