牛客 E - 分割线

牛客 E - 分割线

极角排序,叉积

题目简述

给定平面上 n(n≤100)个互不相同的点。定义“分割线”为:由两个点确定的直线,使得分布在直线两侧的点数量相同(直线上的点不计入任何一侧)。求满足条件的无序点对数量。


核心思路

1. 直接枚举点对(暴力法)

由于 n 很小(≤100),可以直接枚举所有点对 (i, j),然后判断其他点在该直线两侧的分布是否相等。

  • 用叉积判断每个点相对于直线 (i→j) 的位置:
    • cross = (x_k - x_i) * (y_j - y_i) - (y_k - y_i) * (x_j - x_i)
    • cross > 0:左侧
    • cross < 0:右侧
    • cross == 0:在直线上
  • 统计 left 和 right,若相等则答案加一。

复杂度 O(n³),对于 n=100 约 50 万次运算,完全可行。


2. 极角排序(更高效的做法,适用于更大 n)

虽然本题暴力足够,但若数据更大(如 n≤2000),可采用极角排序+双指针做到 O(n² log n)。

步骤

  1. 枚举直线上的一个点 P。
  2. 以 P 为原点,计算其他点的极角(atan2 或向量排序),并排序。
  3. 复制数组并加上 2π 以处理跨半圆的情况。
  4. 用双指针维护小于 180° 的点数,即直线一侧的点数。
  5. 同时需处理共线点(相同极角),统计直线上的点数 onLine。
  6. 判定条件:左侧点数 == 右侧点数,即 left == (n - 2 - onLine) / 2(需满足整数)。

但本题 n 很小,暴力法更为简洁,不易出错。


公式推导(供参考)

对于固定点对 (i, j),设:

  • left = 直线左侧点数
  • right = 直线右侧点数
  • onLine = 除 i,j 外还在直线上的点数(即 cross == 0 的其他点)

总点数满足:
left + right + onLine = n - 2

条件要求 left == right,即 2 * left + onLine = n - 2,等价于 (n - 2 - onLine) 为偶数。


#include <bits/stdc++.h>
using namespace std;
using ll = long long;

void solve() {
    int n; cin >> n;
    vector<pair<ll, ll>> p(n);
    for(int i = 0; i < n; i ++) cin >> p[i].first >> p[i].second;

    int ans = 0;
    for(int i = 0; i < n; i ++) {
        for(int j = i + 1; j < n; j ++) {
            ll dx = p[j].first - p[i].first;
            ll dy = p[j].second - p[i].second;

            int left = 0, right = 0;
            for(int k = 0; k < n; k ++) {
                if(k == i || k == j) continue;
                // 叉积:(p[k] - p[i]) × (p[j] - p[i])
                ll cross = (p[k].first - p[i].first) * dy - (p[k].second - p[i].second) * dx;
                if(cross > 0) left ++;
                else if(cross < 0) right ++;
            }
            if(left == right) ans ++;
        }
    }

    cout << ans << "\n";
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr);
    int _ = 1; //cin >> _;
    while(_ --) solve();
    return 0;
}

posted @ 2026-10-03 09:39  thenrify  阅读(10)  评论(0)    收藏  举报