NumberOfDiscIntersections O(N) solution
Task
We draw N discs on a plane. The discs are numbered from 0 to N − 1. An array A of N non-negative integers, specifying the radiuses of the discs, is given. The J-th disc is drawn with its center at (J, 0) and radius A[J].
We say that the J-th disc and K-th disc intersect if J ≠ K and the J-th and K-th discs have at least one common point (assuming that the discs contain their borders).
The figure below shows discs drawn for N = 6 and A as follows:
A[0] = 1 A[1] = 5 A[2] = 2 A[3] = 1 A[4] = 4 A[5] = 0
![]()
There are eleven (unordered) pairs of discs that intersect, namely:
- discs 1 and 4 intersect, and both intersect with all the other discs;
- disc 2 also intersects with discs 0 and 3.
Write a function:
int solution(vector<int> &A);
that, given an array A describing N discs as explained above, returns the number of (unordered) pairs of intersecting discs. The function should return −1 if the number of intersecting pairs exceeds 10,000,000.
Write an efficient algorithm for the following assumptions:
- N is an integer within the range [0..100,000];
- each element of array A is an integer within the range [0..2,147,483,647].
Solution 1

Create a list of the left hand edges.
Create a list of the right hand edges.
for each left hand edge (lh)
overlap := number of right hand (rh) edges >= lh edge
count += overlap
- It counts a disc as overlapping itself
- It counts discs as overlapping where A is to the right of B, and the rh of A is therefore to the right of the lh of B
N + (N-1) + … +1 = N(N+1)/2
lhes:= a sorted list of the left hand edges
rhes:= a sorted list of the right hand edges
rhindex := 0
count:= 0
for lhs = each left hand edge, from smallest to largest
while (rhes[rhindex++] < lhs); // note final rh edge has to be >= last lh edge, so this will always terminate
rhEdgesCount := rhes.size – rhindex
count += rhEdgesCount
return (count – N(N+1)/2)
-
- #include <algorithm>
- #include <cassert>
- #include <vector>
-
- int solution(vector<int> &A) {
- const int MAX_COUNT = 10000000;
- // create sorted arrays of left-hand edges, and rh edges
- vector<int64_t> lhes;
- vector<int64_t> rhes;
- ulong N = A.size();
- for (size_t i = 0; i < N; i++) {
- // for all prev discs – is rhs intersecting with lhs?
- long lhs = static_cast<long>(i) – A[i];
- lhes.push_back(lhs);
- long rhs = A[i] + static_cast<long>(i);
- rhes.push_back(rhs);
- }
-
- std::sort(lhes.begin(), lhes.end());
- std::sort(rhes.begin(), rhes.end());
-
- long count = 0;
- // now we remove ones where circle centre is to right or same pos. of circle. all of these will be counted in the loop
- // below sum = n(n+1)/2. note includes each item as intersecting itself, so is N + (N-1) + …
- count -= (N + 1) * N / 2;
- size_t rhIndex = 0;
-
- // for each lh edge, count number of rh edges >= it
- for (size_t i = 0; i < lhes.size(); i++) {
- int64_t lhs = lhes[i];
- while (rhes[rhIndex] < lhs)
- rhIndex++;
- if (rhIndex == rhes.size())
- break;
- // add all circles where rh edge >= current left hand edge
- count += rhes.size() – rhIndex;
- if (count > MAX_COUNT)
- return -1;
- }
- assert(count <= MAX_COUNT);
- return static_cast<int>(count);
- }
Results
This scores 100% on codility for both correctness and performance. The complexity is O(N * log(N)) and is dominated by the sorting.
Correctness | 100% |
Performance | 100% |
Complexity | O(N * log(N)) |
Solution 2
For right hand edges that are >=N it is not necessary to store the exact value, only that they are >=N (because it is not possible for a lh edge to be >=N).
This means the rh sides can be stored in an array [0..N-1], with the elements of the array having a count of the number of edges at that point.
The same can be done with the lh edges.
clear lhcount, rhcount
For radius in A
lhe := max(0,centre – radius)
lhcount[lhe]++
rhe:= min(N-1,centre+radius)
rhcount[rhe]++
count := 0
rhSum := 0
for i := N-1 downto 0
rhSum += rhcount[i]
count += lhcount[i] * rhsum
return (count – N(N+1)/2)
-
- #include <cassert>
- int solution(vector<int> &A) {
- const int MAX_COUNT = 10000000;
- ulong N = A.size();
- // create arrays of frequency of left-hand edges, and rh edges
- vector<int> lhes(N,0);
- vector<int> rhes(N,0);
- for (size_t i = 0; i < N; i++) {
- // for all discs – count instances of edges at points
- ulong lhs = static_cast<ulong>(max(0, static_cast<int>(i) – A[i]));
- lhes[lhs]++;
- ulong rhs = min(N – 1, static_cast<ulong>(A[i]) + i);
- rhes[rhs]++;
- }
- long count = 0;
- // now we remove ones where circle center is to right or same pos. of circle. all of these will be counted by loop
- // below sum = n(n+1)/2. note includes 0 and N so N + (N-1) + …
- count -= (N + 1) * N / 2;
- ulong rhSum = 0;
- for (long i = N – 1; i >= 0; i–) {
- // for all prev discs – is rhs intersecting with lhs?
- rhSum += rhes[i];
- count += lhes[i] * rhSum;
- if (count > MAX_COUNT)
- return -1;
- }
-
- assert(count <= MAX_COUNT);
- return static_cast<int>(count);
- }
Results
This scores 100% on codility for both correctness and performance. The time complexity is O(N).
Correctness | 100% |
Performance | 100% |
Complexity | O(N) |
Other Solutions
- Firstly, build arrays of the number of lh edges and rh edges at each position 0..N-1, as above
- Then iterate from 0..N-1, using these arrays to keep track of how many discs are overlapping at the current point.
- For each lh edge, add on the number of overlapping discs to the count
- If there are > 1 lh edges at a point, we also need to add on the number of new overlaps caused by these new discs overlapping each other. For r new discs this is
r(r-1)/2
(the number of combinations of 2 items out of r).
-
- #include <cassert>
- int solution(vector<int> &A) {
- const int MAX_COUNT = 10000000;
- size_t N = A.size();
- // create arrays of frequency of left-hand edges, and rh edges
- vector<int> lhes(N,0);
- vector<int> rhes(N,0);
- for (size_t i = 0; i < N; i++) {
- // for all discs – count instances of edges at points
- int lhs = max(0, static_cast<int>(i) – A[i]);
- lhes[lhs]++;
- int rhs = min(N – 1, static_cast<int>(A[i]) + i);
- rhes[rhs]++;
- }
- int count = 0;
- int overlapping = 0;
- for (size_t i = 0; i < N; i++) {
- // find number of new discs at this point
- int newDiscs = lhes[i];
- count += newDiscs * overlapping;
- count += newDiscs * (newDiscs – 1) / 2;
- if (count > MAX_COUNT)
- return -1;
- // update with number of new discs, minus number now passed
- overlapping += newDiscs – rhes[i];
- }
- assert(count <= MAX_COUNT);
- return count;
- }
Correctness | 100% |
Performance | 100% |
Complexity | O(N) |
Test Function
This code tests the solution
-
- int main() {
- vector<int> A9(100000, 0);
- assert(solution(A9) == 0);
-
- vector<int> A2{};
- assert(solution(A2) == 0);
-
- vector<int> A1{1, 5};
- assert(solution(A1) == 1);
-
- vector<int> A7{1, 5, 2, 1};
- assert(solution(A7) == 5);
-
- vector<int> A6{1, 5, 2};
- assert(solution(A6) == 3);
-
- vector<int> A{1, 5, 2, 1, 4, 0};
- assert(solution(A) == 11);
-
- vector<int> A3{1, 5, 2, 1, 4, 0, 0};
- assert(solution(A3) == 13);
- vector<int> A4{1, 5, 2, 1, 4, 0, 0, 0};
- assert(solution(A4) == 14);
- vector<int> A5{1, 5, 2, 1, 4, 0, 0, 0, 0};
- assert(solution(A5) == 15);
-
- vector<int> A8{1, 2147483647, 0};
- assert(solution(A8) == 2);
- }