这题考的计算几何知识难度不大,只需要会求叉积和判垂直以及判断点是否在直线上即可。题目的数据范围很小(),暴力枚举五个点的时间复杂度是肯定会T的,需要优化到即可通过这个题。问题就变成了通过维护一个什么样的数据结构来降低枚举的时间复杂度,一开始的想法是把所有的线段先算出来,然后从中任取一条线段,通过线段的两个端点作垂线,判断是否有其他的点在这两条垂线上,如果存在就算出他到线段的距离,每一对距离相等的点相连构成的新线段就能和选出的线段组成一个矩形,再去求线段的中垂线,看有多少个点位于中垂线上。
但是仔细想想这样子的时间复杂度是不合理的,线段的数量是级别的,所有时间复杂度应该是会T。而且距离一条线段距离相等的两个点不一定是在线段的同一侧,所以合理的解法是选择一条线段,枚举他两侧的点,一侧的点用来构成矩形,另外一侧的点用来做房子的顶点。这个做法好像是和官方的题解很像,但是感觉写起来会有点麻烦。
我的写法是队友给的一个思路,用向量来表示一个矩形的两条平行边,可以发现这两个向量是一样的。所以可以把开个map把所有相同的向量存在一起,去枚举两条相同向量对应的线段,然后取出四个端点(建议按照题目中给的图的顺序来命名端点,不容易把端点弄混)。假设我们现在取出的线段和,我们只需要判断是否为直角就能判断出这两条线段是否可以构成一个矩形。再去枚举点,按题目中给的条件如果,那么就可以构成一个房子。按照这种枚举方法我们枚举了和与可以构成房子,还会枚举到了和与构成房子,也就是每个房子将会被计算两次,需要将最终答案除2。
#include<bits/stdc++.h>
#define pb push_back
#define fixed(s) fixed<<setprecision(2)<<s
using namespace std;
typedef double db;
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<db, db> PDD;
const int N = 310;
const db EPS = 1e-9;
int sign(db a){ return a < -EPS ? -1 : a > EPS; }
int cmp(db a, db b){ return sign(a - b); }
struct Point{
db x, y;
//int id;
Point(db x_ = 0, db y_ = 0) : x(x_), y(y_) {}
};
struct Line {
Point a;
Point b;
Line(Point a_ = Point(), Point b_ = Point()) : a(a_), b(b_) {}
};
Point operator+ (Point a, Point b){ return {a.x + b.x, a.y + b.y}; }
Point operator- (Point a, Point b){ return {a.x - b.x, a.y - b.y}; }
Point operator* (Point a, db b){ return {a.x * b, a.y * b}; }
Point operator* (db a, Point b){ return {a * b.x, a * b.y}; }
Point operator/ (Point a, db b){ return {a.x / b, a.y / b}; }
db operator* (Point a, Point b){ return a.x * b.x + a.y * b.y; }
db operator^ (Point a, Point b){ return a.x * b.y - a.y * b.x; }
bool operator!= (Point a, Point b) { return a.x != b.x || a.y != b.y; }
bool operator== (Point a, Point b) { return a.x == b.x && a.y == b.y; }
bool operator< (Point a, Point b){
if (a.x == b.x) return a.y < b.y;
return a.x < b.x;
}
int quad(Point a){ return sign(a.y) == 1 || (sign(a.y) == 0 && sign(a.x) >= 0);}
db dot(Point a, Point b){ return a.x * b.x + a.y * b.y; };
db cross(Point a, Point b){ return a.x * b.y - a.y * b.x; }
db cross(Point p1, Point p2, Point p0){ return cross(p1 - p0, p2 - p0); }
db area(Point a, Point b, Point c){ return cross(b - a, c - a); }
bool onLine(Point a, Point b, Point c){ return sign(cross(b, a, c)) == 0; }
bool onLine(Point p, Line l){ return onLine(p, l.a, l.b); }
//向量旋转90度后的新向量
Point rotate(Point p1, Point p2){
Point vec = p1 - p2;
return {-vec.y, vec.x};
}
db get_dist2(Point a, Point b){
db dx = a.x - b.x;
db dy = a.y - b.y;
return dx * dx + dy * dy;
}
Line midSegment(Line l){
Point mid = (l.a + l.b) / 2;
return {mid, mid + rotate(l.a, l.b)};
}
Point p[N * 2];
Line l[N * N];
int main()
{
int n; cin >> n;
for (int i = 0; i < n; i++){
db x, y; cin >> x >> y;
p[i] = {x, y};
}
auto check = [&](Point p, Line l){
if (l.a.x == l.b.x){
return (l.a.x == p.x || l.b.x == p.x);
}
return onLine(p, l);
};
int ans = 0;
map<Point, vector<Line>> mp;
for (int i = 0; i < n; i++){
for (int j = i + 1; j < n; j++){
Point vec1 = p[i] - p[j], vec2 = p[j] - p[i];
mp[vec1].push_back({p[i], p[j]}), mp[vec2].push_back({p[j], p[i]});
}
}
for (auto [T, vec] : mp){
int sz = vec.size();
// A:vec[i].a B: vec[i].b C:vec[j].a D:vec[j].b
for (int i = 0; i < sz; i++){
for (int j = 0; j < sz; j++){
if (i == j) continue;
Point A = vec[i].a, B = vec[i].b, D = vec[j].a, C = vec[j].b;
if (dot(B - A, D - A) == 0){
Line mid = midSegment({A, B});
for (int k = 0; k < n; k++){
if (check(p[k], mid) && dot(A - D, p[k] - D) < 0){
ans++;
}
}
}
}
}
}
cout << ans / 2;
return 0;
}