题意
给定 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;
}