PRMQ Editorial(Unofficial) [Simplicity gauranteed]

PROBLEM LINK:

Practice
Contest

DIFFICULTY:

EASY-MEDIUM

PREREQUISITES:

Segment Trees, Binary Search

PROBLEM:

Given an array a of N integers and Q queries where each query provides you with four integers L, R, X and Y.
For each query, output the value of F(L, R, X, Y) as count the number of primes that lie in the range x to y and that divides the number between the range l to r of A[i].

F(L, R, X, Y)

 result := 0
 for( i = X to i = Y )  {
     if( isPrime(i) ) {
         for( j = L to j = R ) {
              number := a[j]
              exponent := 0
              while( number % i == 0 ) {
                 exponent := exponent + 1 
                 number := number/i
              }
              result := result + exponent
          }
     }
 }
 return result

QUICKK EXPLANATION:

If you are new to Segment Trees then THIS will definitely help you get over segment tree.This is by far the best blog i have came across while reading segment trees. But if you still face hard time understanding this then you can refer to This and This videos.
This problem basically boils down to count the number of primes that lie in the range x to y and that divides the number between the range l to r of A[i]. So for that we can keep track of the prime factors of every A[i].

EXPLANATION:

As 2<=A[i]<=10^6 so maximum number of prime factors can be 2x3x5x7x11x13x17x19 which exceeds 10^6. So what i did here is calculated the prime factors of all the input numbers and pushed them into an array of vectors named v. Each vector in this array stores the prime factors of the corresponding to A[i]. Now i created a segment tree using this array of vectors. While merging 2 nodes all the prime factors are inserted in sorted order of a leaf node in the segment tree so that  we can do binary search on the prime factors to count them between the numbers x and y. This is my first blog, kindly point out the mistakes if any.
Coming to the code here i have taken my node as the array of vectors.

Node of segment Tree

Here each leaf node contains a vector which has all the prime factors of a particular A[i] in sorted order.

Merging of segment nodes

while merging we are just taking the union of 2 sorted vectors.

Querying the segment tree

In each query first i am finding the segment i.e. l to r which is required by that particular query. Now after i have found such a segment i.e. now i have vector that contains the prime factors of all the numbers in the range l to r of A[i]. Now i just need to find x and y in this vector as the difference of their index will give me the count of prime divisors for that query.

ALTERNATIVE SOLUTION:

Please share any other approach which is better then mine or suggest any improvements in my current approach.

EDITORIALIST’S SOLUTIONS:

Can be found here.

9 Likes

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.

I applied the same approach, but instead of using vector i used map to keep record of prime divisors and their corresponding powers.
First i used segment tree but only scored 10 marks. you can check my solution here.
but after it i used fenwick tree for optimization but still scored only 30 marks. could anyone suggest me what’s wrong in my solutions. here is my solution.

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

Very nice work, simplicity at its best. Thanks for this editorial . Hope you will put up more like this :slight_smile:

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.

2 Likes

Hey @meooow , can you please help me at a Q here- https://discuss.codechef.com/questions/101843/regarding-c-stl-set

Its regarding C++ STL.

Very good and simple to understand approach! Great Job!

@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

I used persistent segment tree. https://www.codechef.com/viewsolution/14223288

@vijju123 sure, I’ll take a look!

Hi @d4dev . Thanks Because of you i learnt segment trees for the first time. Can you once explain me the query part if you don’t mind.
Thanks.

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

Hi @d4dev, your solution is very well explained without increasing the complexity of any operation. I applied the same approach but was able to score points only for Subtask 1 & 2 only and I was getting WA for all the testcases in the last subtask. Could you have a look at my code and telling me what am I doing wrong. Here is My Code.

Thanks

can anyone tell if why my solution is not working…[here][1]

In a node,i have stored vector of pair where first is prime factor and second is the power.

struct node{
	vector<pair<int,int> >pr;
	int size;
};

i have submitted it around 35 times but no luck.Please help me.
[1]: https://www.codechef.com/viewsolution/14317892

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