公開日 :
更新日 :
点を「原点から見た向き」の順に並べること。
距離は関係なく、x 軸の正の向きから反時計回りに何度回った位置にあるか(偏角)で並べます。
sort(p.begin(), p.end(), [](const P& a, const P& b) {
return atan2l(a.y, a.x) < atan2l(b.y, b.x);
});まず平面を \( 2 \) つに分け、同じ側どうしのときだけ外積で比べる。
同じ側の \( 2 \) 点は \( 180^\circ \) 未満しか離れていないので、外積が正しく働く。
上半分(half = 0)
0° ≦ 偏角 < 180°
───────────●────────── x 軸
180° ≦ 偏角 < 360°
下半分(half = 1)
x 軸の正の部分(0°)は上半分、負の部分(180°)は下半分に入れる\( x \) 軸の正の部分 \( (0^\circ ) \) は上半分、負の部分 \( (180^\circ ) \) は下半分に入れる。
struct P { ll x, y; }
ll cross(const P& a, const P& b) { return a.x * b.y - a.y * b.x; }
// 0° <= 偏角 < 180° なら 0, 180° <= 偏角 < 360° なら 1
int half(const P& p) {
if(0 < p.y || (p.y == 0 && 0 < p.x) {
return 0;
} else {
return 1;
}
}
bool arg_less(const P& a, const P& b) {
if(half(a) != half(b)) {
return half(a) < half(b);
} else {
return 0 < cross(a, b);
}
}
sort(p.begin(), p.end(), arg_less);これで \( (0^\circ ) \) から反時計回りの順に並びます。
half を変えるだけ。
int half(const P& p){ if(p.y < 0){
return 0;
} else {
return 1;
}
}| 何の外積か | 座標の大きさ | 外積の大きさ | 型 |
| 点そのもの(原点中心) | \( \leq 10^9 \) | \( \leq 2 \times10^{18} \) | long long でOK |
| 点の差 p[j] – p[i] (ある点中心) | \( \leq 2 \times 10^9 \) | \( \leq 8 \times10^{18} \) | long long でギリギリ |
| それより大きい | __int128 |
long long 上限は約 \( 9.2 \times 10^{18} \) 。
偏角は一周するので、「ある角度の範囲に入る点」を数えるときは、配列を \(2\) 周分あると思って添字を \(% n\) で回す。
// 各 i について、偏角が [θi, θi + 180°) の点の個数
int r = 0;
for (int i = 0; i < n; i++) {
r = max(r, i + 1);
while (r < i + n && cross(p[i], p[r % n]) > 0) r++;
// r - i 個が入っている
}