[BZOJ2338][HNOI2011]数矩形(计算几何)

考虑两条线段能够成为矩形的两对角线的条件:两条线段互相平分且相等。
首先预处理出所有点两两构成线段的中点和长度,然后以中点的 x x 坐标为第一关键字,中点的y坐标为第二关键字,线段长度为第三关键字将线段排序。
这样,所有中点坐标和长度都相等的线段集合就是一段连续的区间。
在合法的集合内,可枚举两条线段,用叉积判断面积。
Q:为什么这样复杂度是对的?
A:最坏情况下,有 k k 条线段交于一点,形成一个圆,这k条线段都是圆的直径。由于所有点的坐标都是整数,因此 k k 的范围很小,以致于O(n2k)的复杂度能通过。
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 1505, M = 225e4 + 5;
struct cyx {
    int x1, y1, x2, y2, cx, cy; ll len;
    cyx() {}
    cyx(int _x1, int _y1, int _x2, int _y2, int _cx, int _cy, ll _len) :
        x1(_x1), y1(_y1), x2(_x2), y2(_y2), cx(_cx), cy(_cy), len(_len) {}
} a[M];
inline bool comp(const cyx &a, const cyx &b) {
    if (a.cx != b.cx) return a.cx < b.cx;
    if (a.cy != b.cy) return a.cy < b.cy;
    return a.len < b.len;
}
ll area(int x, int y) {
    int x1 = a[y].x1 - a[x].x1, y1 = a[y].y1 - a[x].y1,
        x2 = a[y].x2 - a[x].x1, y2 = a[y].y2 - a[x].y1;
    return abs(1ll * x1 * y2 - 1ll * x2 * y1);
}
int n, m, X[N], Y[N]; ll Ans;
int main() {
    int i, j, k; n = read();
    for (i = 1; i <= n; i++) X[i] = read(), Y[i] = read();
    for (i = 1; i < n; i++) for (j = i + 1; j <= n; j++)
        a[++m] = cyx(X[i], Y[i], X[j], Y[j], X[i] + X[j], Y[i] + Y[j],
            1ll * (X[i] - X[j]) * (X[i] - X[j]) + 1ll *
            (Y[i] - Y[j]) * (Y[i] - Y[j]));
    sort(a + 1, a + m + 1, comp); for (k = 1; k <= m;) {
        int nxt = k; while (a[k].cx == a[nxt].cx && a[k].cy == a[nxt].cy
            && a[k].len == a[nxt].len && nxt <= m) nxt++;
        for (i = k; i < nxt; i++) for (j = i + 1; j < nxt; j++)
            Ans = max(Ans, area(i, j)); k = nxt;
    }
    cout << Ans << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值