Official

E - Trapezium Editorial by en_translator


Overview of solution

First, count how many pairs of parallel edges there are. To count them, find the slope between each of \(\frac{N(N-1)}{2}\) possible pairs, and for each slope value \(x\):

  • add \(\frac{c_x(c_x-1)}{2}\) to the answer, if there are \(c_x\) pairs with slope \(x\).

Here, a trapezium that is a parallelogram is counted twice each, and a trapezium that is not parallelogram is counted once each. Therefore, we need to subtract the number of parallelograms from the answer. A trapezium is a parallelogram if and only if the two diagonals’ midpoints coincide, so find the midpoint between each of \(\frac{N(N-1)}{2}\) possible pairs, and for each midpoint coordinates \(y\):

  • subtract \(\frac{d_y(d_y-1)}{2}\) from the answer, if there are \(d_y\) pairs with midpoint \(y\).

Now every trapezium is counted exactly once, so this algorithm yields a correct answer.

Implementation

If you compute the slope as a floating-point value, precision errors may cause wrong coincidence verdict. Also, slopes like \(1/0\) and \(-1/0\) cause issues. Computing as rational numbers make it easy.

For more details, refer to the following sample code.

Sample code (C++)

#include <iostream>
#include <cstdio>
#include <vector>
#include <array>
#include <set>
#include <map>
using std::cin;
using std::cout;
using std::cerr;
using std::endl;
using std::pair;
using std::make_pair;
using std::vector;
using std::min;
using std::max;
using std::array;
using std::set;
using std::map;

#include <atcoder/all>
using mint = atcoder::modint998244353;
using atcoder::segtree;

typedef long long int ll;

ll n;
vector<ll> x, y;

ll mygcd (ll a, ll b) {
	if (b == 0) return a;
	return mygcd(b, a % b);
}
ll myabs (ll x) {
	return (x >= 0) ? x : (-x);
}

vector<pair<ll, ll> > slopes;
vector<pair<ll, ll> > centers;
ll countsamepair (const vector<pair<ll, ll> > &vec) {
	ll sum = 0;
	ll idx = 0;
	while (idx < vec.size()) {
		ll j = idx;
		while (j < vec.size() && vec[j] == vec[idx]) j++;

		ll len = j - idx;
		sum += len * (len-1) / 2;

		idx = j;
	}
	return sum;
}
void solve () {
	for (ll i = 0; i < n; i++) {
		for (ll j = i+1; j < n; j++) {
			// slope
			ll px = x[j] - x[i];
			ll py = y[j] - y[i];

			if (px == 0) {
				px = 0;
				py = 1;
			} else if (py == 0) {
				px = 1;
				py = 0;
			} else {
				if (px < 0) {
					px *= -1;
					py *= -1;
				}
				ll g = mygcd(myabs(px), myabs(py));
				px /= g;
				py /= g;
			}

			slopes.push_back({px, py});

			// center
			centers.push_back({x[i]+x[j], y[i]+y[j]});
		}
	}

	ll ans = 0;
	sort(slopes.begin(), slopes.end());
	ans += countsamepair(slopes);
	sort(centers.begin(), centers.end());
	ans -= countsamepair(centers);

	cout << ans << "\n";

	
	return;
}

int main (void) {
	std::ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin >> n;
	x.resize(n);
	y.resize(n);
	for (ll i = 0; i < n; i++) {
		cin >> x[i] >> y[i];
	}

	solve();

	return 0;
}

posted:
last update: