LOJ 3054: 洛谷 P5286: 「HNOI2019」鱼

题目传送门:LOJ #3054

题意简述

略。

题解

鱼尾和鱼身分开考虑。

考虑固定 \(D\) 点,其它点按照极角排序。

按照极角序枚举 \(A\) 点,然后双指针,可以算出对于每个 \(A\) 可行的鱼尾数量(\(AD\) 方向为半平面的法向量方向,半平面中的点按照与 \(D\) 的距离分类统计)。

然后再考虑确定 \(B, C\),首先可以发现 \(BC \perp AD\),所以先获取要求的 \(BC\) 的斜率信息。

\(\mathcal O (n^2)\) 的时间内预处理出每一对 \(BC\) 并按照斜率分类,具体可以用最简分数存储。

在每一类中再以中点所在直线分类,那么中点必须在 \(AD\) 上,找到那一类后再使用二分可以获得合法的 \(BC\) 数量。

与鱼尾相乘后相加,再乘以 \(4\) 即可得到答案。

#include <cstdio>
#include <algorithm>
#include <vector>
#include <tuple>
#include <map>

typedef long long LL;

inline LL Abs(LL x) { return x < 0 ? -x : x; }
LL Gcd(LL a, LL b) { return b ? Gcd(b, a % b) : a; }

struct Vec {
	LL x, y;
	Vec() { x = y = 0; }
	Vec(LL _x, LL _y) { x = _x, y = _y; }
};
inline bool operator < (Vec a, Vec b) { return a.x == b.x ? a.y < b.y : a.x < b.x; }
inline Vec operator + (Vec a, Vec b) { return Vec(a.x + b.x, a.y + b.y); }
inline Vec operator - (Vec a, Vec b) { return Vec(a.x - b.x, a.y - b.y); }
inline Vec operator * (LL a, Vec b) { return Vec(a * b.x, a * b.y); }
inline LL operator * (Vec a, Vec b) { return a.x * b.x + a.y * b.y; }
inline LL operator / (Vec a, Vec b) { return a.x * b.y - a.y * b.x; }
inline LL len(Vec a) { return a * a; }
inline Vec Rotate(Vec a) { return Vec(-a.y, a.x); }

typedef std::pair<std::pair<LL, LL>, LL> plll;
const int MN = 1005, MS = 499505;

int N, Cnt;
Vec P[MN];
std::map<plll, int> Num;
std::vector<Vec> D[MS];
Vec S[MN * 2];
std::map<LL, int> Buk;
LL Ans;

inline void getABC(Vec A, Vec B, LL &a, LL &b, LL &c) {
	a = B.y - A.y, b = B.x - A.x, c = A / B;
	LL d = Gcd(Abs(a), Gcd(Abs(b), Abs(c)));
	a /= d, b /= d, c /= d;
	if (a < 0 || (a == 0 && b < 0)) a = -a, b = -b, c = -c;
}

int main() {
	scanf("%d", &N);
	for (int i = 1; i <= N; ++i) scanf("%lld%lld", &P[i].x, &P[i].y);
	for (int j = 2; j <= N; ++j)
		for (int i = 1; i < j; ++i) {
			Vec A = P[i], B = P[j], C = A + B;
			Vec tA = C + Rotate(2 * A - C);
			Vec tB = C + Rotate(2 * B - C);
			LL a, b, c;
			getABC(tA, tB, a, b, c);
			int &val = Num[{{a, b}, c}];
			if (!val) val = ++Cnt;
			D[val].push_back(C);
		}
	for (int i = 1; i <= Cnt; ++i) std::sort(D[i].begin(), D[i].end());
	for (int u = 1; u <= N; ++u) {
		int M = 0;
		for (int i = 1; i <= N; ++i) if (i != u) S[++M] = P[i] - P[u];
		std::sort(S + 1, S + M + 1, [&](Vec i, Vec j) {
			int zi = i.y > 0 || (i.y == 0 && i.x > 0);
			int zj = j.y > 0 || (j.y == 0 && j.x > 0);
			if (zi == zj) return i / j > 0;
			return zi < zj;
		});
		for (int i = 1; i <= M; ++i) S[M + i] = S[i];
		LL Sum = 0;
		int lb = 1, rb = 0;
		Buk.clear();
		for (int i = 1; i <= M; ++i) {
			while (rb < i + M - 1) {
				Vec R = S[rb + 1];
				if (R * S[i] >= 0 && (R / S[i] > 0 || (R / S[i] == 0 && rb >= M))) break;
				Sum += Buk[len(R)]++;
				++rb;
			}
			while (lb <= i + M - 1) {
				Vec R = S[lb];
				if (R * S[i] < 0 || (R / S[i] > 0 || (R / S[i] == 0 && lb > M))) break;
				Sum -= --Buk[len(R)];
				++lb;
			}
			Vec A = P[u], B = S[i] + P[u];
			if (B < A) std::swap(A, B);
			LL a, b, c, Val = 0;
			getABC(2 * A, 2 * B, a, b, c);
			plll p = {{a, b}, c};
			if (Num.find(p) != Num.end()) {
				auto V = D[Num[p]];
				Val = std::lower_bound(V.begin(), V.end(), 2 * B) - std::upper_bound(V.begin(), V.end(), 2 * A);
			}
			Ans += Sum * Val;
		}
	}
	printf("%lld\n", Ans * 4);
	return 0;
}
posted @ 2020-03-30 13:54  粉兔  阅读(532)  评论(0编辑  收藏  举报