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
- 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.
- 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.
- 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.
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';
}