CF660D 平行四边形
1 CF660D 平行四边形
2 题目描述
时间限制 \(4s\) | 空间限制 \(256M\)
给出同一个平面上的 \(n\) 个点,所有的点都分散,没有任何三个点在同一条直线上。找出以这些点为顶点的平行四边形的个数。
3 题解
平行四边形的一个判定就是一组对边平行且相等。我们可以 \(O(n^2)\) 枚举有序的两个节点,计算这两个节点 \((x_i ,y_i)\) 和 \((x_j, y_j)\) 之间的那条线段的斜率和那条线段的长度。斜率就是 \(\frac{x_i - x_j}{y_i - y_j}\),长度就是 \(\sqrt{(x_j - x_i)^2 + (y_j - y_i)^2}\)。然后,我们用 \(map\) 存储这两个元素,如果存在别的线段的两个元素与这个完全相同,那么这四个节点就可以组成一个平行四边形。如果有两个点的纵坐标相同,那么就单独记录一下这些线段的斜率。这里有个小 \(trick\),由于我们用 \(map\) 时并不在意具体的数值,只在意是否相同,所以长度我们可以只记录 \((x_j - x_i)^2 + (y_j - y_i)^2\) 即可,避免丢失精度。
注意:我们对于每一个平行四边形都会从横竖两组边统计两边,所以最后的答案要记得除以 \(2\)。
4 代码(空格警告):
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
const int N = 2005;
#define int long long
int n, ans;
struct node
{
int x, y;
}a[N];
map< pair<int, int>, int > m;
int get(int x1, int y1, int x2, int y2)
{
return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
}
signed main()
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i].x >> a[i].y;
for (int i = 1; i <= n; i++)
{
for (int j = i+1; j <= n; j++)
{
if (a[i].y - a[j].y == 0) m[make_pair(-1, abs(a[i].x - a[j].x))]++;
else m[make_pair((a[i].x - a[j].x) * 100000000 / (a[i].y - a[j].y), get(a[i].x, a[i].y, a[j].x, a[j].y))]++;
}
}
for (int i = 1; i <= n; i++)
{
for (int j = i+1; j <= n; j++)
{
if (a[i].y - a[j].y == 0)
{
m[make_pair(-1, abs(a[i].x - a[j].x))]--;
ans += m[make_pair(-1, abs(a[i].x - a[j].x))];
}
else
{
m[make_pair((a[i].x - a[j].x) * 100000000 / (a[i].y - a[j].y), get(a[i].x, a[i].y, a[j].x, a[j].y))]--;
ans += m[make_pair((a[i].x - a[j].x) * 100000000 / (a[i].y - a[j].y), get(a[i].x, a[i].y, a[j].x, a[j].y))];
}
}
}
cout << ans / 2;
return 0;
}