题意
给定 nnn 个平面上的点,求是否存在 333 个点使得它们组成的三角形面积为 SSS。需要输出三个点的坐标。
n≤2000n\le2000n≤2000。
解法
暴力做法:枚举 333 个点,海伦公式判断面积是否相等。复杂度 O(n3)O(n^3)O(n3)。优化思路即为:对于先枚举的两个点 A,BA,BA,B 组成的连线 lll,那么第 333 个点 CCC 就要满足其到这条连线的距离为 2m∣l∣\cfrac{2m}{|l|}∣l∣2m。如果其余点与 lll 的距离有序,那么就可以 O(logn)O(\log n)O(logn) 的复杂度二分了,分成在 lll 之上、之下两个部分。
考虑将所有点进行旋转,使 lll 与 yyy 轴重合。这时候可以发现一个性质:
- 以 lll 为纵轴时,对于任意两个点 X,YX,YX,Y,它们的横坐标大小关系,只与原图中 lll 与 ABABAB 的斜率有关。
然后做法就出来了:先把 n2n^2n2 条连线按照斜率从小到大排序,然后从小到大枚举,这样任意两点旋转后的横坐标大小关系恰好会变化一次。枚举连线的过程中每次交换两个端点,再在连线的两侧(正负)进行查找即可。
代码
#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;
}

573

被折叠的 条评论
为什么被折叠?



