Related
#include <iostream>
using namespace std;
int main()
{
const int ARRAY_SIZE = 10;
int value[ARRAY_SIZE] = { 1, 2, 3, 4, 3, 4, 2, 3, 5, 6};
int value2[100];
for (int i = 0; i < ARRAY_SIZE; i++)
{
for (int j = i + 1; j <= ARRAY_SIZE; j++)
{
if (value[i] == value[j])
{
cout << value[i] << " ";
}
}
}
return 0;
}
The output is
2 3 3 4 3
How can I make the output become 2 3 4 ?
Edit: I'm trying to print all numbers appearing more than once in the value array
I think I should create one more array to store value, but I stuck with it and don't know how to do it.
It helps considerably to sort your array. Then you only need two indices:
a write index, starting at 0
a read index, starting at 1
Loop over your array using the read index. For every array[read] that has a duplicate value at array[read-1], IFF that value also does not exist at array[write], then copy it over and increment your write index.
Finally, the new length of your data is equal to your write index.
The basic idea is this: if I have a sorted list of values:
1 3 3 4 5 5 5 7 7 9
Then I can easily see if a value is a duplicate or not by comparing the current (read) with the previous.
┌────┐
1 3 3 │4 5│ 5 5 7 7 9 -- not duplicates
└────┘
↑
┌────┐
1 3 3 4 │5 5│ 5 7 7 9 -- duplicate values
└────┘
↑
The only remaining trick is to make just a single copy of that value to the write index, which we can do by simply looking at the last thing we wrote.
You can use a std::map to count the number of times a value is in the array. If the number appears 2 or more times, then print it.
#include <iostream>
#include <map>
int main()
{
const int ARRAY_SIZE = 10;
int value[ARRAY_SIZE] = { 1, 2, 3, 4, 3, 4, 2, 3, 5, 6};
std::map <int, int> mp;
for(int i : value)
++mp[i];
for(const auto& p : mp)
if(p.second > 1)
std::cout << p.first << ' ';
}
Link.
I built a recursive function that reads a vector as input and returns a new vector in which every two consecutive elements are switched. For example input: 1 2 3 4 5 6 and output: 2 1 4 3 6 5. The thing I don't get is that when I write the function this way:
vector<int> reverse(vector<int> v) {
if (v.size() < 2)
return v;
else
{
int pos1 = v.at(0); //= 1
int pos2 = v.at(1); //= 2
v.erase(v.begin()); //v = 2 3 4 5 6
v.erase(v.begin()); //v = 3 4 5 6
vector<int> rev = reverse(v);
rev.push_back(pos2); //rev = 2
rev.push_back(pos1); //rev = 2 1
return rev;
}
}
i get 6 5 4 3 2 1. I know the vector::push_back() adds the elements at the end of the vector, so why not 2 1 4 3 6 5? When I wrote it this way it gave me the good answer though (2 1 4 3 6 5) but idk why:
vector<int> reverse(vector<int> v) {
if (v.size() < 2)
return v;
else
{
int pos1 = v.at(v.size() - 2); //= 5
int pos2 = v.at(v.size() - 1); //= 6
v.pop_back(); //v = 1 2 3 4 5
v.pop_back(); //v = 1 2 3 4
vector<int> rev = reverse(v); //call the recursive function
rev.push_back(pos2); //rev = 5
rev.push_back(pos1); //rev = 6 5
return rev;
}
}
The main() function is this:
int main() {
vector<int> w;
int zahl;
cout << "Please give the vector or any letter to end the input: "<< endl;
while (cin >> zahl)
{
w.push_back(zahl);
}
for (int elem : reverse(w))
{
cout << elem << ", ";
}
return 0;
}
It's an easy fix.
The problem with your code is that the recursive step does not correctly translate a correctly constructed sub-result to a slightly larger one.
You would want to do:
// Correctly create the head of the result.
vector<int> rev = {pos2, pos1};
// Now you want to handle the tail, and assuming the output of reverse
// is correct for smaller lists, this would be achieved by appending
// the rest.
const auto sub = reverse(v);
rev.insert(rev.end(), sub.begin(), sub.end());
This ensures that if your list starts with 1 2, it would turn into a list with 2 1, followed by a correctly processed tail.
Your second code worked because you were processing the sequence in reverse, so your recursive step was in fact correct.
The result you obtain is incorrect because in the last call to your function you return an empty vector, and then you push at the back of this vector your last pair of numbers inverted, so 6,5, so you do proceding back in the call stack.
Let's have a look to what you have at every call (first call, second call, ecc.):
v=[1,2,3,4,5,6] ->recursive call
v=[3,4,5,6] ->recursive call
v=[5,6] ->recursive call
v=[] ->return empty vector
rev=[], push back the first two elements of v in reverse order, so rev=[6,5] ->return rev
rev=[6,5], push back the first two elements of v in reverse order, so rev=[6,5,4,3] ->return rev
rev=[6,5,4,3], push back the first two elements of v in reverse order, so rev=[6,5,4,3,2,1]
For this reason, you need first to have a vector with the first two numbers reversed and then attach the processed tail, as suggested above.
Please note that you are constructing copies of the vector every time you call the function. You can create a reversed copy without modifying the copy of the argument given as argument. I would use iterators instead, so that every time your function "sees" a smaller portion of the original vector, without touching it. Here an implementation:
vector<int> reverse(vector<int>::const_iterator begin, vector<int>::const_iterator end) {
if (begin==end-1) //the vector has an odd number of elements, let's append the last element
return vector<int>(1, *(end-1));
else if(begin>=end) //the vector has en even number of elements
return vector<int>();
else
{
vector<int> pairWiseReversed({*(begin+1), *begin});
vector<int> tail=reverse(begin+2, end);
pairWiseReversed.insert(pairWiseReversed.end(), tail.begin(), tail.end());
return pairWiseReversed;
}
}
vector<int> buildReversed(const vector<int>& input){
return reverse(input.begin(), input.end());
}
And here a main:
int main() {
vector<int> v{1,2,3,4,5,6};
cout<<"Input vector"<<endl;
for(auto n:v)
cout<<n<<" ";
cout<<endl;
vector<int> res=buildReversed(v);
cout<<"Output vector"<<endl;
for(auto n:res)
cout<<n<<" ";
cout<<endl;
return 0;
}
How to divide elements in an array into a minimum number of arrays such that the difference between the values of elements of each of the formed arrays does not differ by more than 1?
Let's say that we have an array: [4, 6, 8, 9, 10, 11, 14, 16, 17].
The array elements are sorted.
I want to divide the elements of the array into a minimum number of array(s) such that each of the elements in the resulting arrays do not differ by more than 1.
In this case, the groupings would be: [4], [6], [8, 9, 10, 11], [14], [16, 17]. So there would be a total of 5 groups.
How can I write a program for the same? Or you can suggest algorithms as well.
I tried the naive approach:
Obtain the difference between consecutive elements of the array and if the difference is less than (or equal to) 1, I add those elements to a new vector. However this method is very unoptimized and straight up fails to show any results for a large number of inputs.
Actual code implementation:
#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
int main() {
int num = 0, buff = 0, min_groups = 1; // min_groups should start from 1 to take into account the grouping of the starting array element(s)
cout << "Enter the number of elements in the array: " << endl;
cin >> num;
vector<int> ungrouped;
cout << "Please enter the elements of the array: " << endl;
for (int i = 0; i < num; i++)
{
cin >> buff;
ungrouped.push_back(buff);
}
for (int i = 1; i < ungrouped.size(); i++)
{
if ((ungrouped[i] - ungrouped[i - 1]) > 1)
{
min_groups++;
}
}
cout << "The elements of entered vector can be split into " << min_groups << " groups." << endl;
return 0;
}
Inspired by Faruk's answer, if the values are constrained to be distinct integers, there is a possibly sublinear method.
Indeed, if the difference between two values equals the difference between their indexes, they are guaranteed to belong to the same group and there is no need to look at the intermediate values.
You have to organize a recursive traversal of the array, in preorder. Before subdividing a subarray, you compare the difference of indexes of the first and last element to the difference of values, and only subdivide in case of a mismatch. As you work in preorder, this will allow you to emit pieces of the groups in consecutive order, as well as detect to the gaps. Some care has to be taken to merge the pieces of the groups.
The worst case will remain linear, because the recursive traversal can degenerate to a linear traversal (but not worse than that). The best case can be better. In particular, if the array holds a single group, it will be found in time O(1). If I am right, for every group of length between 2^n and 2^(n+1), you will spare at least 2^(n-1) tests. (In fact, it should be possible to estimate an output-sensitive complexity, equal to the array length minus a fraction of the lengths of all groups, or similar.)
Alternatively, you can work in a non-recursive way, by means of exponential search: from the beginning of a group, you start with a unit step and double the step every time, until you detect a gap (difference in values too large); then you restart with a unit step. Here again, for large groups you will skip a significant number of elements. Anyway, the best case can only be O(Log(N)).
I would suggest encoding subsets into an offset array defined as follows:
Elements for set #i are defined for indices j such that offset[i] <= j < offset[i+1]
The number of subsets is offset.size() - 1
This only requires one memory allocation.
Here is a complete implementation:
#include <cassert>
#include <iostream>
#include <vector>
std::vector<std::size_t> split(const std::vector<int>& to_split, const int max_dist = 1)
{
const std::size_t to_split_size = to_split.size();
std::vector<std::size_t> offset(to_split_size + 1);
offset[0] = 0;
size_t offset_idx = 1;
for (std::size_t i = 1; i < to_split_size; i++)
{
const int dist = to_split[i] - to_split[i - 1];
assert(dist >= 0); // we assumed sorted input
if (dist > max_dist)
{
offset[offset_idx] = i;
++offset_idx;
}
}
offset[offset_idx] = to_split_size;
offset.resize(offset_idx + 1);
return offset;
}
void print_partition(const std::vector<int>& to_split, const std::vector<std::size_t>& offset)
{
const std::size_t offset_size = offset.size();
std::cout << "\nwe found " << offset_size-1 << " sets";
for (std::size_t i = 0; i + 1 < offset_size; i++)
{
std::cout << "\n";
for (std::size_t j = offset[i]; j < offset[i + 1]; j++)
{
std::cout << to_split[j] << " ";
}
}
}
int main()
{
std::vector<int> to_split{4, 6, 8, 9, 10, 11, 14, 16, 17};
std::vector<std::size_t> offset = split(to_split);
print_partition(to_split, offset);
}
which prints:
we found 5 sets
4
6
8 9 10 11
14
16 17
Iterate through the array. Whenever the difference between 2 consecutive element is greater than 1, add 1 to your answer variable.
`
int getPartitionNumber(int arr[]) {
//let n be the size of the array;
int result = 1;
for(int i=1; i<n; i++) {
if(arr[i]-arr[i-1] > 1) result++;
}
return result;
}
`
And because it is always nice to see more ideas and select the one that suites you best, here the straight forward 6 line solution. Yes, it is also O(n). But I am not sure, if the overhead for other methods makes it faster.
Please see:
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <iterator>
using Data = std::vector<int>;
using Partition = std::vector<Data>;
Data testData{ 4, 6, 8, 9, 10, 11, 14, 16, 17 };
int main(void)
{
// This is the resulting vector of vectors with the partitions
std::vector<std::vector<int>> partition{};
// Iterating over source values
for (Data::iterator i = testData.begin(); i != testData.end(); ++i) {
// Check,if we need to add a new partition
// Either, at the beginning or if diff > 1
// No underflow, becuase of boolean shortcut evaluation
if ((i == testData.begin()) || ((*i) - (*(i-1)) > 1)) {
// Create a new partition
partition.emplace_back(Data());
}
// And, store the value in the current partition
partition.back().push_back(*i);
}
// Debug output: Copy all data to std::cout
std::for_each(partition.begin(), partition.end(), [](const Data& d) {std::copy(d.begin(), d.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << '\n'; });
return 0;
}
Maybe this could be a solution . . .
How do you say your approach is not optimized? If your is correct, then according to your approach, it takes O(n) time complexity.
But you can use binary-search here which can optimize in average case. But in worst case this binary search can take more than O(n) time complexity.
Here's a tips,
As the array sorted so you will pick such a position whose difference is at most 1.
Binary search can do this in simple way.
int arr[] = [4, 6, 8, 9, 10, 11, 14, 16, 17];
int st = 0, ed = n-1; // n = size of the array.
int partitions = 0;
while(st <= ed) {
int low = st, high = n-1;
int pos = low;
while(low <= high) {
int mid = (low + high)/2;
if((arr[mid] - arr[st]) <= 1) {
pos = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
partitions++;
st = pos + 1;
}
cout<< partitions <<endl;
In average case, it is better than O(n). But in worst case (where the answer would be equal to n) it takes O(nlog(n)) time.
In a recent problem where i have to sum all values at common indexes in all possible subsets of size k in array of size n.
For eg: If
array ={1,2,3}
Its subsets (k=2) will be (x [i] , x [j]) where i < j
1 2
1 3
2 3
Sum:4,8
Firstly I have used recursion (same that of generating all subsets)
int sum_index[k]={0};
void sub_set(int array[],int n,int k,int temp[],int q=0,int r=0)
{
if(q==k)
{
for(int i=0;i<k;i++)
sum_index[i]+=temp[i];
}
else
{
for(int i=r;i<n;i++)
{
temp[q]=array[i];
sub_set(value,n,k,temp,q+1,i+1);
}
}
}
Problem is its taking too much time then expected .
Then i modified it to...
void sub_set(int array[],int n,int k,int temp[],int q=0,int r=0)
{
if(q==k)
{
return;
}
else
{
for(int i=r;i<n;i++)
{
temp[q]=array[i];
sum_index[q]+=temp[q]; //or sum_index[q]+=s[i];
sub_set(value,n,k,temp,q+1,i+1);
}
}
}
Still taking too much time!!
Is there any other approach to this problem?? Or any other modification i needed that i am unaware of??
Instead of iterating through the possible sub-sets, think of it a combinatorics problem.
To use your example of k=2 and {1,2,3}, let's just look at the first value of the result. It has two 1's and one 2. The two 1's correspond to the number one element sets that can be made from {2, 3} and the one 2 corresponds to the number of one element sets that can be made from {3}. A similar arrangement exists for the one 2 and two 3's in the second element of the result and looking at the subsets of the elements that appear before the element being considered.
Things get a bit more complicated when k>2 because then you will have to look for the number of combinations of elements before and after the element being considered, but the basic premise still works. Multiply the number of possible subsets before times the number of subsets afterwards and that will tell you how many times each element contributes to the result.
A solution in O(n^2) instead of O(n!):
First a tiny (:)) bit of explanation, then some code:
I´m going to assume here that your array is sorted (if not, use std::sort first). Additionally, I´m going to work with the array values 1,2,3,4... here, if you array consists arbitrary values (like 2 8 17), you´ll have to think of it as the indices (ie. 1=>2, 2=>8 etc.)
Definition: (x choose y) means the binomial coefficient, how it is calculated is in the link too. If you have an array size a and some k for the subset size, (a choose k) is the number of permutations, eg. 3 for your example: (1,2), (1,3) and (2,3).
You want the sum for each column if you write the permutations under each other, this would be easy if you knew for each column how many times each array element occurs, ie. how many 1´s, 2´s and 3´s for the first, and how many for second column (with k=2).
Here a bigger example to explain: (1,2,3,4,5) and all possible k´s (each in one block):
1
2
3
4
5
12
13
14
15
23
24
25
34
35
45
123
124
125
134
135
145
234
235
245
345
... (didn´t write k=4)
12345
Let´s introduce column indices, 0<=c<k, ie. c=0 means the first column, c=1 the second and so on; and the array size s=5.
So, looking eg. at the k=3-block, you´ll notice that the lines beginning with 1 (column c=0) have all permutations of the values (2,3,4,5) for k=2, more generally a value x in column c has all permutations for values x+1 to s after it. The values from from x+1 to s are s-x different values, and after column c there are k-c-1 more columns. So, for a value x, you can calculate ((s-x) choose (k-c-1)).
Additionally, the first column has only the values 1,2,3, the last two numbers are not here because after this column there are two more columns.
If you do this for the first column, it works well. Eg. with value 1 in the first column of k=3 above:
count(x) = ((s-x) choose (k-c-1)) = (4 choose 2) = 6
and indeed there are six 1 there. Calculate this count for every array value, multiply x*count(x), and sum it up for every x, that´s the result for the first column.
The other columns are a tiny bit harder, because there can be multiple "permutation blocks" of the same number. To start with, the step above needs a small adjustment: You need a muliplier array somewhere, one multiplier for each array value, and in the beginning each multiplier is 1. In the calculation x*count(x) above, take x*count(x)*muliplier(x) instead.
In the k=3-example, 1 in the first column can be followed by 2,3,4, 2 can be followed by 3,4, and 3 by 4. So the 3-based permutations of the second column need to be counted twice, and the 4-based even three times; more generally so many times like there are smaller values in the previos colums. Multiply that to the current multiplier.
...
Some code:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
// factorial (x!)
unsigned long long fact(unsigned char x)
{
unsigned long long res = 1;
while(x)
{
res *= x;
x--;
}
return res;
}
//binomial coefficient (n choose k)
unsigned long long binom(unsigned char n, unsigned char k)
{
if(!n || !k) return 1;
return (fact(n) / fact(k)) / fact(n-k);
}
//just for convenience
template<class T> void printvector(std::vector<T> data)
{
for(auto l : data) cout << l << " ";
cout << endl;
}
std::vector<unsigned long long> calculate(std::vector<int> data, int k)
{
std::vector<unsigned long long> res(k, 0); //result data
std::vector<unsigned long long> multiplier(data.size(), 1);
if(k < 1 || k > 255 || data.size() < 1) return res; //invalid stuff
std::sort(data.begin(), data.end()); //as described
for(int column = 0; column < k; column++) //each column separately
{
//count what to multiply to the multiplier array later
std::vector<unsigned long long> newmultiplier(data.size(), 0);
//for each array element in this column
for(int x = column; x <= (data.size() + column - k); x++)
{
//core calculation
res[column] += data[x] * multiplier[x] * binom(data.size() - x - 1, k - column - 1);
//counting the new multiplier factor
for(int helper = x + 1; helper < data.size(); helper++)
newmultiplier[helper]++;
}
//calculating new multiplier
for(int x = 0; x < data.size(); x++)
{
if(newmultiplier[x])
multiplier[x] *= newmultiplier[x];
}
}
return res;
}
int main() {
printvector(calculate({1,2,3}, 2)); //output 4 8
return 0;
}
std::next_permutation may help:
std::vector<int> sub_set(const std::vector<int>& a, int k)
{
std::vector<int> res(k, 0);
std::vector<bool> p(a.size() - k, false);
p.resize(a.size(), true);
do
{
int index = 0;
for (std::size_t i = 0; i != p.size(); ++i) {
if (p[i]) {
res[index++] += a[i];
}
}
} while (std::next_permutation(p.begin(), p.end()));
return res;
}
Live Demo
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
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 ?
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:
Don't update each time from li to ri.
instead just increase the count at each starting position and at one more than end position because you have to include till end.
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
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)
This is what I'd do:
Created a (hash)map index->count. Go through all queries and for each index in the range, increment the count (*).
Order the elements in the array by size, decending (called values now).
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.
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