0

I am trying to solve this problem :

Little girl has an array of n elements (the elements of the array are indexed starting from 1).

Also, there are "q" queries, each one is defined by a pair of integers li, ri (1 ≤ li ≤ ri ≤ n). You need to find for each query the sum of elements of the array with indexes from li to ri, inclusive.

The little girl found the problem rather boring. She decided to reorder the array elements before replying to the queries in a way that makes the sum of query replies maximum possible. Your task is to find the value of this maximum sum.


Input:

The first line contains two space-separated integers n (1 ≤ n ≤ 10^5) and q (1 ≤ q ≤ 10^5) — the number of elements in the array and the number of queries, correspondingly.

The next line contains n space-separated integers ai (1 ≤ ai ≤ 10^5) — the array elements.

Each of the following q lines contains two space-separated integers li and ri (1 ≤ li ≤ ri ≤ n) — the i-th query.


Output:

In a single line print a single integer — the maximum sum of query replies after the array elements are reordered.

Sample testcases:

input:
3 3
5 3 2
1 2
2 3
1 3
output
25

input
5 3
5 2 4 1 3
1 5
2 3
2 3
output
33

I have knowledge of Segment tree so i applied Lazy propagation approach through segment tree.

My Effort code :

#include <iostream>
#include <string>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <set>
#include <list>
#include <cmath>
#include <stack>
using namespace std;

#define scan(x) scanf("%d",&x)
#define print(x) printf("%d ",x)
#define println(x) printf("%d\n",x)
#define For(i,a,j,k) for (i = a; i < j; i+= k)
#define For_back(i,a,j,k) for (i = j; i >= a; i-= k)
#define SET(a,x) memset(a,x,sizeof(a))
#define mod 1000000007
#define inf 0x7fffffff
#define Max 2000000

typedef pair<int,int> pii;
typedef pair<pii,int> piii;

long long int tree[3*Max];
long long int lazy[3*Max];

void update(int node,int a,int b,int i,int j,long long int value)
{
    if (lazy[node]!= 0)
    {
        tree[node] += lazy[node];

        if (a != b)
        {
            lazy[2*node] += lazy[node];
            lazy[2*node+1] += lazy[node];
        }
        lazy[node] = 0;
    }
    if (a > b || a > j || b < i)
        return;
    if (a >= i && b <= j)
    {
        tree[node] += value;
        if (a != b)
        {
            lazy[2*node] += value;
            lazy[2*node+1] += value;
        }
        return;
    }
    int mid = (a+b)/2;
    update(2*node,a,mid,i,j,value);
    update(2*node+1,mid+1,b,i,j,value);

    tree[node] = (tree[2*node]+tree[2*node+1]);
}

long long int query(int node,int a,int b,int i,int j)
{
    if (a> b || a > j || b < i) return 0;

    if (lazy[node] != 0)
    {
        tree[node] += lazy[node];
        if (a != b)
        {
            lazy[2*node] += lazy[node];
            lazy[2*node+1] += lazy[node];
        }
        lazy[node] = 0;
    }
    if (a >= i && b <= j)
        return tree[node];
    int mid = (a+b)/2;
    long long int q1 = query(2*node,a,mid,i,j);
    long long int q2 = query(2*node+1,mid+1,b,i,j);

    return ((q1+q2));
}
int main()
{
    SET(lazy,0);
    SET(tree,0);

    int n,m;
    cin >> n >> m;
    int i,j;
    int arr[n];
    For(i,0,n,1)
    {
        cin >> arr[i];
    }
    sort(arr,arr+n);
    For(i,0,m,1)
    {
        long long int num1,num2;
        cin >> num1 >> num2;

        update(1,0,n-1,num1-1,num2-1,1);
    }
    long long int my[n];
    For(i,0,n,1)
    {   
        long long int number = query(1,0,n-1,i,i);       
        my[i] = number;
    }
    sort(my,my+n);
    long long int sum = 0;
    For_back(i,0,n-1,1){
        sum += my[i]*arr[i];
    }
    cout << sum << endl;
    return 0;   
}

My approach for this was simple just to do as said using segment tree and lastly print the answer.

My question is is there any simpler Algo for this ? or should i optimize my segment tree code ?

3 Answers3

1

I think this will work - comments invited

Create an array of dimension n called count and initialize it to 0 Go through the Q array

For each query - From li to ri increment count by 1 i.e. the count of elements li to ri Sort the array n Sort the count array (remember the index) Pick up the highest of the count and at the corresponding index put the highest element from N Continue this for all elements

Basically we are ensuring that the highest element occurs the highest number of times (when referred by the query)

Mukul Joshi
  • 324
  • 2
  • 4
1

This is what I'd do:

  1. Created a (hash)map index->count. Go through all queries and for each index in the range, increment the count (*).
  2. Order the elements in the array by size, decending (called values now).
  3. Extract the counts from your hashmap (the indexes are irrelevant now, because we don't need them again and the array is reordered accordingly) and also order them decending.
  4. Iterate through the ordered array of counts and sum up sum += counts[i] * values[i]

let's say your array is

0, 1, 2, 3, 4, 5, 6, 7, 8, 9

queries are:

q1: 1-3
q2: 2-4
q3: 3-5

the map:

1->1
2->2
3->3
4->2
5->1

the counts sorted:

3,2,2,1

one of the perfect reorderings (irrelevant for the algorithm, because only the sum is required)

6,7,9,8,5,4,3,2,1,0

sum for queries:

(6 + 7 + 9) + (7 + 9 + 8) + (9 + 8 + 5) = 68

with the algorithm:

3 * 9 + 2 * 8 + 2 * 7 + 1 * 6 + 1 * 5 = 68

(*) If you want to speed this up, you can use an array / vector of size n instead of a map and use indexes as keys. If just mentioned the map in my example because it makes the idea more evident

b.buchhold
  • 3,837
  • 2
  • 24
  • 33
1

Concept: "You have to fix the biggest element from array to the index that is queried most times and then second biggest element to second most queried element

Here's implementation of my method:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
using namespace std;
int main()
{
    LL n,q,l,r,i;
    cin>>n>>q;
    LL arr[n];
    for(i=0;i<n;i++)
        cin>>arr[i];
    LL freq[n];
    memset(freq,0,sizeof freq);
    sort(arr,arr+n);
    for(i=0;i<q;i++)
    {
        cin>>l>>r;
        freq[l-1]++; // DP method of freq
        if(r<n)     
        freq[r]--;
    }
    for(i=1;i<n;i++)
        freq[i]+=freq[i-1];
    sort(freq,freq+n);
    LL ans=0;
    for(i=n-1;i>=0;i--)
        if(freq[i])
            ans+=arr[i]*freq[i];
    cout<<ans<<endl;
    return 0;
}

Yeah you have to sort the array and then sort frequency and then multiply number with frequency and that will result in maximum sum..

Method to keep count:

  1. Don't update each time from li to ri.
  2. instead just increase the count at each starting position and at one more than end position because you have to include till end.
  3. lastly sum all count. in O(n). and you can know how many time each was increased. sort it and sort the given array and multiply number with frequency so obtained and you have you answer.

input: 5 3
array : 5 2 4 1 3
1st query: 1 5
freq update = 1 0 0 0 0 
2nd query: 2 3
freq update =1 1 0 -1 0 
3rd query: 2 3
freq update= 1 2 0 -2 0 
collective freq=1 3 3 1 1 
sorted freq= 1 1 1 3 3 
sorted array =1 2 3 4 5 
ans =33
S J
  • 3,210
  • 1
  • 22
  • 32
  • why you did -- at "r" not "r-1" ? please explain this part ? your algorithm seems to good and working! i tested it is 4 time faster than mine ! –  Jun 18 '13 at 14:56
  • 1st of all. i have used 0-index based array. As i said we have to update at ri+1 postion means ri in 0-index based array. r-- is done in sense that i have to keep count only till end. so when counting from start to end. when you sum up all frequency. when negative is encountered that increment is not carried away for rest of array it only lasts for that part of array.. try it on paper. May be then you can understand better ! – S J Jun 18 '13 at 15:00
  • yep! you example shows that.. nice method ! Thanks for helping :) –  Jun 18 '13 at 15:03
  • remember this counting method ! sometimes in array queries this method can work around easily in Array Queries Related Problem. else you have all time Fav. Segment tree ! – S J Jun 18 '13 at 15:06
  • Yeah. Binary indexed tree are useful too ! –  Jun 18 '13 at 15:13
  • @Shashank_Jain Great method of counting. I instantly thought about a segment tree, but this is so simple! In addition, you can use Count Sort for sorting both arrays to achieve even better complexity. – pkacprzak Jun 18 '13 at 16:42
  • Yes. but many times in programming contest. limit is generally 1<=a<=10^9. so count sort isn't a good option for that as memory limit is only till 256MB. – S J Jun 18 '13 at 17:09
  • @Shashank_Jain in this given problem Count Sort is faster, but of course using built-in sort is quicker to code. – pkacprzak Jun 18 '13 at 17:12
  • 1
    yeah range is till 10^5. i didn't see that.. yeah now solution can be more fast! – S J Jun 18 '13 at 17:13