cppAcademy
FROM LEARNING TO COMPETING

Real problems.
Your next challenge.

Explore the Group C Olympiad and Autumn Tournament archive.
Choose a topic. Find a challenge. Build your solution.

274 tasks: 213 from the Municipal, Regional, and National Olympiad rounds (2009–2026), 57 Autumn Tournament tasks (2007–2025), and 4 previously featured problems. 207 tasks have independently written English summaries, analyses, and C++17 solutions; the remaining entries link to Arena. Difficulty labels are learning estimates, not official ratings. Special formats and source clarifications are explained on affected tasks.
GROUP C2021 · MediumGeometry and sorting

Count Rectangles from Their Diagonals

Autumn Tournament (2021)

Submission name: rect.cpp

Problem summary

Given planar points, count the different rectangles whose four vertices are among them. Sides need not be parallel to the coordinate axes. Rectangles differ when their vertex sets differ.

Input

N, followed by N pairs of integer coordinates.

Output

The number of rectangles.

Constraints

3 < N <1000; both coordinates are positive integers below 10000.

Worked example

Original statement sample.

Input

6
6 4
5 3
4 2
1 5
3 7
2 6

Output

3

Solution analysis

  1. A rectangle’s two diagonals have the same midpoint and equal length. Describe each pair of points by (x 1+x 2,y 1+y 2,(x 1−x 2)²+(y 1−y 2)²). The sums represent twice the midpoint, avoiding fractions; squared length avoids square roots.
  2. Generate this key for every unordered pair of distinct points and sort the keys. Any two different pairs in one equal-key group form a rectangle. If a group contains g diagonals, it contributes g(g−1)/2. Each rectangle is counted once because its diagonal pair is unique.
  3. For the sample, the three pairs(6,4)–(1,5), (5,3)–(2,6), and(4,2)–(3,7) all have midpoint(3.5,4.5), but their squared lengths are 26,18,and 26. The two length 26 pairs give one rectangle. The other equal-key groups supply the remaining two, for a total of 3.

Why it works

In a rectangle the diagonals bisect each other and are equal, so every rectangle is included in one group. Conversely, two distinct segments with a common midpoint form a parallelogram when their endpoints are alternated; equal diagonal lengths make that parallelogram a rectangle. With distinct point coordinates, different equal-key segments cannot share an endpoint or be collinear, so no degenerate shape is counted.

Complexity: O(N² log N) time and O(N²) memory. There are N(N−1)/2 keys; generating them is quadratic and sorting dominates. Accumulate group combinations in long long.

Watch out. Do not restrict attention to axis-aligned rectangles or compare floating-point midpoints. Use unordered point pairs once. Identical coordinate duplicates, if supplied, represent the same geometric point and are deduplicated before enumeration.

Commented C++17 solution

An independently written complete program with standard headers.

#include<algorithm>
#include<iostream>
#include<tuple>
#include<utility>
#include<vector>
using namespace std;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin>>n;
    vector<pair<int,int>>points(n);
    for(auto&point:points){
        cin>>point.first>>point.second;
    }
    sort(points.begin(),points.end());
    points.erase(unique(points.begin(),points.end()),points.end());
    n=static_cast<int>(points.size());
    vector<tuple<int,int,long long>>diagonals;
    diagonals.reserve(n*(n-1)/2);
    for(int i=0;i<n;i++){
        for(int j=i+1;j<n;j++){
            long long dx=points[i].first-points[j].first;
            long long dy=points[i].second-points[j].second;
            diagonals.emplace_back(
                points[i].first+points[j].first,points[i].second+points[j].second,dx*dx+dy*dy);
        }
    }
    sort(diagonals.begin(),diagonals.end());
    long long answer=0;
    for(size_t first=0;first<diagonals.size();){
        size_t after=first+1;
        while(after<diagonals.size()&&diagonals[after]==diagonals[first]){
            after++;
        }
        long long count=static_cast<long long>(after-first);
        // Choose the two diagonals of a rectangle from this group.
        answer+=count*(count-1)/2;
        first=after;
    }
    cout<<answer<<'\n';
}

Source: Arena Olimpiici ↗