题意
给定 n n n 个平面上的点,求是否存在 3 3 3 个点使得它们组成的三角形面积为 S S S。需要输出三个点的坐标。
n ≤ 2000 n\le2000 n≤2000。
解法
暴力做法:枚举 3 3 3 个点,海伦公式判断面积是否相等。复杂度 O ( n 3 ) O(n^3) O(n3)。优化思路即为:对于先枚举的两个点 A , B A,B A,B 组成的连线 l l l,那么第 3 3 3 个点 C C C 就要满足其到这条连线的距离为 2 m ∣ l ∣ \cfrac{2m}{|l|} ∣l∣2m。如果其余点与 l l l 的距离有序,那么就可以 O ( log n ) O(\log n) O(logn) 的复杂度二分了,分成在 l l l 之上、之下两个部分。
考虑将所有点进行旋转,使 l l l 与 y y y 轴重合。这时候可以发现一个性质:
- 以 l l l 为纵轴时,对于任意两个点 X , Y X,Y X,Y,它们的横坐标大小关系,只与原图中 l l l 与 A B AB AB 的斜率有关。
然后做法就出来了:先把 n 2 n^2 n2 条连线按照斜率从小到大排序,然后从小到大枚举,这样任意两点旋转后的横坐标大小关系恰好会变化一次。枚举连线的过程中每次交换两个端点,再在连线的两侧(正负)进行查找即可。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2005;
#define ll long long
int n, a[maxn + 5];
ll m;
struct Point {
int id, x, y;
} p[maxn + 5];
struct Segment {
int x, y;
double k;
bool operator< (const Segment &o) const { // 按照斜率
return k < o.k;
}
} sid[maxn * maxn + 5];
bool cmp(const Point &A, const Point &B) {
return A.x < B.x;
}
ll cal(const Point &u,const Point &v,const Point &w) { // 算出第 3 条边的长度
return abs(1ll * (u.x - v.x) * (u.y - w.y) - 1ll * (u.x - w.x) * (u.y - v.y));
}
bool ok = false;
// 对于 l 进行左右两侧的二分。
void F1(int l, int r, const Point &A, const Point &B) {
while (l <= r) {
int mid = l + r >> 1;
ll S = cal(p[mid], A, B);
if (S == 2 * m) {
printf("Yes\n%d %d\n%d %d\n%d %d\n", A.x, A.y, B.x, B.y, p[mid].x, p[mid].y);
ok = true; return ;
} else if (S < 2 * m) r = mid - 1;
else l = mid + 1;
}
}
void F2(int l, int r, const Point &A, const Point &B) {
while(l <= r) {
int mid = l + r >> 1;
ll S = cal(p[mid], A, B);
if (S == 2 * m) {
printf("Yes\n%d %d\n%d %d\n%d %d\n", A.x, A.y, B.x, B.y, p[mid].x, p[mid].y);
ok = true; return ;
} else if (S < 2 * m) l = mid + 1;
else r = mid - 1;
}
}
int tot = 0;
int main() {
scanf("%d%lld", &n, &m);
for (int i = 1; i <= n; i ++)
scanf("%d%d", &p[i].x, &p[i].y);
sort(p + 1, p + n + 1, cmp);
for (int i = 1; i <= n; i ++)
a[i] = p[i].id = i;
for (int i = 1; i <= n; i ++)
for (int j = i + 1; j <= n; ++j)
sid[++ tot] = Segment{i, j, 1.0 * (p[j].y - p[i].y) / (p[j].x - p[i].x)};
sort(sid + 1, sid + tot + 1);
for (int i = 1; i <= tot; i ++) {
int mn = min(a[sid[i].x], a[sid[i].y]);
int mx = max(a[sid[i].x], a[sid[i].y]);
swap(p[mn], p[mx]); swap(a[sid[i].x], a[sid[i].y]);
F1(1, mn - 1, p[a[sid[i].x]], p[a[sid[i].y]]);
if (ok) return 0;
F2(mx + 1, n, p[a[sid[i].x]], p[a[sid[i].y]]);
if (ok) return 0;
}
return puts("No"), 0;
}