题目传送门: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
", Ans * 4);
return 0;
}