Блог пользователя Hamada14

Автор Hamada14, история, 8 лет назад, По-английски

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.

  • Проголосовать: нравится
  • +7
  • Проголосовать: не нравится

»
8 лет назад, # |
  Проголосовать: нравится +7 Проголосовать: не нравится
»
8 лет назад, # |
  Проголосовать: нравится +3 Проголосовать: не нравится

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

»
8 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

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 лет назад, # |
Rev. 3   Проголосовать: нравится +20 Проголосовать: не нравится

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 лет назад, # |
  Проголосовать: нравится +13 Проголосовать: не нравится

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 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Didn't understand. Code snippet please!

    • »
      »
      »
      8 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Code
    • »
      »
      »
      8 лет назад, # ^ |
      Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

      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 лет назад, # |
  Проголосовать: нравится +5 Проголосовать: не нравится

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 лет назад, # ^ |
    Rev. 9   Проголосовать: нравится +12 Проголосовать: не нравится

    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 лет назад, # |
  Проголосовать: нравится +5 Проголосовать: не нравится

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; }