Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Points and segments

I'm doing online course and got stuck at this problem.

The first line contains two non-negative integers 1 ≤ n, m ≤ 50000 — the number of segments and points on a line, respectively. The next n lines contain two integers a_i ≤ b_i defining the i-th segment. The next line contain m integers defining points. All the integers are of absolute value at most 10^8. For each segment, output the number of points it is used from the n-points table.

My solution is :

for point in points:
    occurrence = 0
    for l, r in segments:
        if l <= point <= r:
           occurrence += 1
    print(occurrence),

The complexity of this algorithm is O(m*n), which is obviously not very efficient. What is the best way of solving this problem? Any help will be appreciated!

Sample Input:

2 3
0 5
7 10
1 6 11

Sample Output:

1 0 0

Sample Input 2:

1 3
-10 10
-100 100 0

Sample Output 2:

0 0 1
like image 752
user1806258 Avatar asked Nov 17 '15 08:11

user1806258


People also ask

How many segments are there in 4 points?

Total 6 line segments.

What is a segment example?

A ruler, a pencil, and a stick are examples of a line segment in real life.

How do you find a segment of a point?

To find the coordinates of the point X add the components of the segment ¯PX to the coordinates of the initial point P. So, the coordinates of the point X are (1+2,6−1.25)=(3,4.75).


3 Answers

You can use sweep line algorithm to solve this problem.

First, break each segment into two points, open and close points.

Add all these points together with those m points, and sort them based on their locations.

Iterating through the list of points, maintaining a counter, every time you encounter an open point, increase the counter, and if you encounter an end point, decrease it. If you encounter a point in list m point, the result for this point is the value of counter at this moment.

For example 2, we have:

1 3
-10 10
-100 100 0

After sorting, what we have is:

-100 -10 0 10 100

At point -100, we have `counter = 0`

At point -10, this is open point, we increase `counter = 1`

At point 0, so result is 1

At point 10, this is close point, we decrease `counter = 0`

At point 100, result is 0

So, result for point -100 is 0, point 100 is 0 and point 0 is 1 as expected.

Time complexity is O((n + m) log (n + m)).

like image 198
Pham Trung Avatar answered Jan 02 '23 03:01

Pham Trung


[Original answer] by how many segments is each point used

I am not sure I got the problem correctly but looks like simple example of Histogram use ...

  1. create counter array (one item per point)
  2. set it to zero
  3. process the last line incrementing each used point counter O(m)

  4. write the answer by reading histogram O(n)

So the result should be O(m+n) something like (C++):

const int n=2,m=3;
const int p[n][2]={ {0,5},{7,10} };
const int s[m]={1,6,11};
int i,cnt[n];
for (i=0;i<n;i++) cnt[i]=0;
for (i=0;i<m;i++) if ((s[i]>=0)&&(s[i]<n)) cnt[s[i]]++;
for (i=0;i<n;i++) cout << cnt[i] << " "; // result: 0 1

But as you can see the p[] coordinates are never used so either I missed something in your problem description or you missing something or it is there just to trick solvers ...

[edit1] after clearing the inconsistencies in OP the result is a bit different

By how many points is each segment used:

  1. create counter array (one item per segment)
  2. set it to zero
  3. process the last line incrementing each used point counter O(m)
  4. write the answer by reading histogram O(m)

So the result is O(m) something like (C++):

const int n=2,m=3;
const int p[n][2]={ {0,5},{7,10} };
const int s[m]={1,6,11};
int i,cnt[m];
for (i=0;i<m;i++) cnt[i]=0;
for (i=0;i<m;i++) if ((s[i]>=0)&&(s[i]<n)) cnt[i]++;
for (i=0;i<m;i++) cout << cnt[i] << " "; // result: 1,0,0

[Notes]

After added new sample set to OP it is clear now that:

  • indexes starts from 0
  • the problem is how many points from table p[n] are really used by each segment (m numbers in output)
like image 36
Spektre Avatar answered Jan 02 '23 02:01

Spektre


Use Binary Search.

Sort the line segments according to 1st value and the second value. If you use c++, you can use custom sort like this:

sort(a,a+n,fun);    //a is your array of pair<int,int>, coordinates representing line

bool fun(pair<int,int> a, pair<int,int> b){
    if(a.first<b.first)
        return true;
    if(a.first>b.first)
        return false;
    return a.second < b.second;
}

Then, for every point, find the 1st line that captures the point and the first line that does not (after the line that does of course). If no line captures the point, you can return -1 or something (and not check for the point that does not).

Something like:

int checkFirstHold(pair<int,int> a[], int p,int min, int max){     //p is the point

    while(min < max){
        int mid = (min + max)/2;
        if(a[mid].first <= p && a[mid].second>=p && a[mid-1].first<p && a[mid-1].second<p)   //ie, p is in line a[mid] and not in line a[mid-1]
            return mid;
        if(a[mid].first <= p && a[mid].second>=p && a[mid-1].first<=p && a[mid-1].second>=p)   //ie, p is both in line a[mid] and not in line a[mid-1]
            max = mid-1;
        if(a[mid].first < p && a[mid].second<p )   //ie, p is not in line a[mid]
            min = mid + 1;
    }
return -1; //implying no point holds the line
}

Similarly, write a checkLastHold function.

Then, find checkLastHold - checkFirstHold for every point, which is the answer.

The complexity of this solution will be O(n log m), as it takes (log m) for every calculation.

like image 41
vish4071 Avatar answered Jan 02 '23 03:01

vish4071