ABC418E
解法
四角形と,対辺の組を対応させる. 台形の選び方から平行四辺形の選び方を引けばよい. 一つの平行四辺形に対して,対辺の選び方が2通りあることに注意.
対辺が平行かどうかは,傾きで判断する.ただし,傾きが無限大のときも認める. 傾きは既約分数とする.ただし,無限大は \(0/1\) の形で統一する. 台形は,等しい傾きの2辺を選べば良いから,対辺の選び方を傾きで類別すれば良い. 同様に,平行四辺形の選び方は等しい傾きと長さを選べばよいから,対辺の選び方を傾きと長さで類別すれば良い.
使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"
template<typename T> // T : integers type
class Frac{
public:
T GCD(T x, T y){
x = abs(x);
y = abs(y);
// if(x < y) swap(x,y); // 不要
if(y == 0) {
return x;
}
return GCD(y, x % y);
}
T a, b; // a/b
Frac(){}
Frac(T _a, T _b) : a(_a), b(_b){
if(a == 0 && b == 0) return;
if(b < 0){
b *= -1;
a *= -1;
}
if(b == 0){
a = 1;
} else if(a == 0){
b = 1;
} else{
T g = GCD(a,b);
a /= g;
b /= g;
}
}
bool operator < (const Frac that) const {
return a * that.b < that.a * b;
}
bool operator == (const Frac that) const {
return a == that.a && b == that.b;
}
pair<T,T> to_pair(){ // TLE 対策. Frac を map の domain に載せるのは重いから.
return pair<T,T>(a,b);
}
};
ll dist2(ll a, ll b){
return square(a) + square(b);
}
int main() {
ll n;
cin >> n;
vll x(n), y(n);
rep(i,n){
cin >> x[i] >> y[i];
}
// trapezoid: 台形
// parallelogram: 平行四辺形
map<pll,ll> tra;
map<pair<pll,ll>, ll> para;
rep(i,n) rep(j,i){
ll yy = y[i] - y[j];
ll xx = x[i] - x[j];
Frac<ll> f(yy, xx);
ll d = dist2(xx, yy);
tra[f.to_pair()]++;
para[{f.to_pair(), d}]++;
}
ll ans = 0;
for(auto [_, cnt]: tra){
ans += nC2(cnt);
}
ll tmp = 0;
for(auto [_, cnt]: para){
tmp += nC2(cnt);
}
assert(tmp%2 == 0);
ans -= tmp/2;
cout << ans << endl;
return 0;
}