PRMQ Editorial(Unofficial) [Simplicity gauranteed]

How about this solution

I think everyone is familiar with the brute force solution :

We create a 2D array count of size 100000*78498 and for each arr[i] for (1 <= i <= n) we store the number of times the jth prime number divides it. Then for each j we can calculate contiguous sum for 1 to n and store it in a new array conti_count as shown below.

for(int i = 1; i <= n; i++)
{
        for(int j = 0; j < p.size(); j++)
	        conti_count[i][j] = conti_count[i-1][j] + count[i][j];
}

But now the problem is size of 2D array and complexity of the above code spinet. Can we do something better ?

Alright answer this : How many prime divisor of a number n exists at max which are greater than sqrt(n).

The answer is : 1, because if you divide n with m ( where m >= sqrt(n) ) the result is less than sqrt(n).

Using the above fact we can reduce the code complexity from O(100000*78498) to O(100000*168)` for the pre calculation part. So now what different are we doing from our initial brute force solution ?

We are calculating the above mentioned count only for the prime numbers less than 1000. Now for each query we can iterate from index[max(1,x)] to index[min(1000,y)] and count number of appearance of all prime numbers between 1 to 1000 in range l to r, where index stores the rank of prime number (max value is 168).

What is left is to handle case of calculating number of integers in range l to r with a prime factor greater than max(1000,x) to y. So while doing prime factorisation of each number during precomputation we make an array and for each i in range 1 to n and we store the prime factor of arr[i] which is greater than 1000. Now we can either use trie or merge sort tree to compute number of integers less than equal to y in range l to r and number of integers less than x in range l to r in log(n) each.

You can view my solution for better understanding : PRMQ

1 Like

@d4dev I tried to do this question as told by you. But I am stuck on this for long. I am getting only 20 points. Tried all things but not getting an AC. Can someone please help me with this. @d4dev my solution is similar to yours ,it won’t take much time.

Please Help. Thanks.
Link to my solution CodeChef: Practical coding for everyone

@vijju123,@siddhartp538 Thank you so much for pointing this out. I was struggling with it a lot. Actually, the mistake was :

I was trying to maintain an array sieve[] which keeps the smallest prime factor. In this way any number, prime factors can be found in O(logN) (after sieve). But while applying the sieve, By mistake i was keeping the highest prime factor (overwriting everytime as soon as it get a prime factor).

In simple words say 40. Now this no. has smallest prime factor 2 which i intended to store in sieve[40]. But when loop reaches to 5 sieve[40] was changed to 5.
The only change i need to do was if(sieve[j]==0) sieve[j]=i;
where i is the first and j is the second loop in sieve function. So now only those which are 0 are changed. And there will be no overwriting of prime factors.

And if smallest prime factor is stored you automatically get sorted prime factors of any no. in logN.

Thank you again :slight_smile: Finally an AC.

The explanation is shorter than Problem…

No, i dont mean that explanation should be long. You put across the point well enough. Why not discuss some implementation details, like how you made segment tree, the functions, and etc.

Hi, I viewed your solution but our approaches are different.
See instead of calculating all the prime numbers less then 10^6 we can just calculate the prime factors of each corresponding A[i]. Moreover you have used unordered_map which has o(logn) insertion and o(logn) searching in the worst case. Moreover try to use scanf and printf by including header instead of ios_base::sync_with_stdio(false);. And please go through my solution one more time you will surely understand the difference in our approaches, still if you face difficulty let me know.

Thanks for the suggestion, I have now added a perfect link to explanation of segment tree, moreover now i have also explained the node structure, merging and querying of the segment tree for this particular problem, hope this helps.

OK, i will check your solution once again. and thanks for your response.

After using the ios_base::sync_with_stdio(false); statement, cin and cout works more faster than scanf and printf. so it’s not the issue.and by calculating prime factors <10^6 won’t affect it much. I applied the same approach of prime factorization as you used. and also i think merging of two unordered_map is faster than merging of two vector, since you are using a comparison function.
and yes you are right. your approach is different than mine. As i see you didn’t use loop in query function. which gives the advantage to you.

Please tell me if you find anything incorrect in first paragraph.

1 Like

@d4dev, your information is not accurate. unordered_map has constant time access and insertion in the average case. Moreover, ios_base::sync_with_stdio(false) significantly improves the performance of cin and cout, which makes it comparable to or even faster than scanf and printf. Check here for details.

3 Likes

Hey @meooow , can you please help me at a Q here- Regarding C++ stl- set. - general - CodeChef Discuss

Its regarding C++ STL.

@meooow yes you are right sorry about that.

Indeed, ios_base::sync_with_stdio(false) is fast but if you use endl per query it will go back to being slow because of flushing. Remember to always use \n for multiple queries.

Though that’s the case, your solution will still TLE because of other issues, try following what others said :slight_smile:

2 Likes

@vijju123 sure, I’ll take a look!

Here segment tree contains a sorted vectors of prime numbers of the elements which falls in the range of L to R.
Let me make it clear by an example
Problem:
suppose my array is like 2 3 4 5 4
Here l=1 and r=3
Here x=2 and y=4
Solution:
Now this segment l=1 and r=3 will give a vector like 2 2 2 3
here 1st 2 is for A[1](1 based indexing)
here 2nd and 3rd 2 are because of A[3]
here 3 is because of A[2]
now we will do binary search on this received vector and find the difference in the index corresponding to x and y which will give 4 for this case.
Hope this clears some of your doubts :slight_smile:

1 Like

Thanks for the editorial, I was trying to keep the frequency too, but yours approach is simpler(just keeping whatever the prime factor be as many times).:slight_smile:

@vijju123, @d4dev please have a look at it. It won’t take much time.

Are you sure that the elements in the merged nodes are sorted?

1 Like

@vishesh_345 I have a test case for you but its gonna be very hard for you to debug it.
5
2 45 14 546 475
1
2 5 10 1000
My ac code is giving ans as 2 your code is giving ans as 0.
Actually the problem is that you are applying binary search on an unsorted vector.
Look here: XOqxDU - Online C++0x Compiler & Debugging Tool - Ideone.com

1 Like

Good that you got a AC ^^

@tonny in your approach you can optimize your prime factorization method. You are using an approach O(sqrt(N)/2). You can have a look at my solution.

https://www.codechef.com/viewsolution/14642454
Also it is good to keep the frequency of every prime factor but it is not needed actually. You see total no. of prime factors all numbers in array is with O(N) space complexity because total no. of prime factors cannot exceed 19 (even if all are same that is). So even if you keep primes as it is there is no problem.
Hope it helps :slight_smile: