• 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
    ", Ans * 4);
    	return 0;
    }
    
  • 相关阅读:
    着迷
    网上找的所谓过滤
    最近很郁闷
    属性应用
    新版微软一站式示例代码浏览器全球发布 – Metro 界面带给你全新示例搜索浏览体验
    微软一站式示例代码库 2012示例代码发布第一期
    微软一站式示例代码库 8 月新代码示例发布
    如何在Visual Studio中直接使用示例代码浏览器搜索下载和管理代码示例
    一周最新示例代码回顾 (3/19–3/25)
    微软一站式示例代码库 2012 年2月示例代码更新。8个全新示例为您的开发保驾护航
  • 原文地址:https://www.cnblogs.com/PinkRabbit/p/HNOI2019D1T1.html
Copyright © 2020-2023  润新知