在跑半平面交的时候,要塞一条直线-y进去,避免出现非法情况。
#include <bits/stdc++.h>// from: cpp.json
#define INF 8e18
#define int long long
using namespace std;// 几何模板
template<class T>
struct Point {T x; T y; Point(const T &x_ = 0, const T &y_ = 0) : x(x_), y(y_) {}template<class U>operator Point<U>() {return Point<U>(U(x), U(y));}Point &operator+=(const Point &p) & {x += p.x;y += p.y;return *this;}Point &operator-=(const Point &p) & {x -= p.x;y -= p.y;return *this;}Point &operator*=(const T &v) & {x *= v;y *= v;return *this;}Point &operator/=(const T &v) & {x /= v;y /= v;return *this;}Point operator-() const {return Point(-x, -y);}friend Point operator+(Point a, const Point &b) {return a += b;}friend Point operator-(Point a, const Point &b) {return a -= b;}friend Point operator*(Point a, const T &b) {return a *= b;}friend Point operator/(Point a, const T &b) {return a /= b;}friend Point operator*(const T &a, Point b) {return b *= a;}friend bool operator<(Point a, Point b) {return a.x == b.x ? a.y < b.y : a.x < b.x;}friend bool operator==(const Point &a, const Point &b) {return a.x == b.x && a.y == b.y;}friend std::istream &operator>>(std::istream &is, Point &p) {return is >> p.x >> p.y;}friend std::ostream &operator<<(std::ostream &os, const Point &p) {return os << "(" << p.x << ", " << p.y << ")";}
};template<class T>
struct Line {Point<T> a; Point<T> b; vector<int> id;Line(const Point<T> &a_ = Point<T>(), const Point<T> &b_ = Point<T>()) : a(a_), b(b_) {}
};/*计算向量 a 和 b 的点积(内积)dot(a, b) = a.x*b.x + a.y*b.y
*/
template<class T>
T dot(const Point<T> &a, const Point<T> &b) {return a.x * b.x + a.y * b.y;
}template<class T>
T dot3(const Point<T> &a, const Point<T> &b, const Point<T> &c) {return dot(b - a, c - a);
}/*计算向量 a 和 b 的叉积(2D 向量的“伪叉积”)cross(a, b) = a.x*b.y - a.y*b.x正值表示 b 在 a 的逆时针方向,负值表示顺时针方向
*/
template<class T>
T cross(const Point<T> &a, const Point<T> &b) {return a.x * b.y - a.y * b.x;
}template<class T>
T cross3(const Point<T> &a, const Point<T> &b, const Point<T> &c) {return cross(b - a, c - a);
}/*计算点 p 的平方模:|p|^2 = dot(p, p)
*/
template<class T>
T square(const Point<T> &p) {return dot(p, p);
}template<class T>
T dist2(Point<T> &A, Point<T> &B) {T dx = A.x - B.x;T dy = A.y - B.y;return dx * dx - dy * dy;
}/*计算点 p 的长度(欧几里得范数),返回 double
*/
template<class T>
long double length(const Point<T> &p) {return sqrtl(square(p));
}/*计算线段 l 的长度通过端点之差的长度来计算
*/
template<class T>
long double length(const Line<T> &l) {return length(l.a - l.b);
}/*将向量 p 归一化:返回单位向量 p/|p|注意:调用者需保证 p != (0,0)
*/
template<class T>
Point<T> normalize(const Point<T> &p) {return p / length(p);
}/*判断两条直线(线段) l1, l2 是否平行平行当且仅当方向向量叉积为 0
*/
template<class T>
bool parallel(const Line<T> &l1, const Line<T> &l2) {return cross(l1.b - l1.a, l2.b - l2.a) == 0;
}/*计算两点 a, b 之间的距离,等价于 |a - b|
*/
template<class T>
long double distance(const Point<T> &a, const Point<T> &b) {return length(a - b);
}/*计算点 p 到直线 l(通过 l.a 和 l.b 定义)的垂线距离distancePL = |cross(l.a-l.b, l.a-p)| / |l|
*/
template<class T>
long double distancePL(const Point<T> &p, const Line<T> &l) {return std::abs(cross(l.a - l.b, l.a - p)) / length(l);
}/*计算点 p 到线段 l 的最短距离如果垂足在线段内部,返回垂线距离;否则返回到最近端点的距离
*/
template<class T>
long double distancePS(const Point<T> &p, const Line<T> &l) {// 如果 p 在 l.a 的“后方”,距离为 p 到 l.aif (dot(p - l.a, l.b - l.a) < 0) {return distance(p, l.a);}// 如果 p 在 l.b 的“后方”,距离为 p 到 l.bif (dot(p - l.b, l.a - l.b) < 0) {return distance(p, l.b);}// 否则垂足在线段内部,使用垂线距离return distancePL(p, l);
}/*将点 (x, y) 绕原点逆时针旋转 90 度,得到 (-y, x)
*/
template<class T>
Point<T> rotate(const Point<T> &a) {return Point(-a.y, a.x);
}/*对向量 a 做象限分类:- 如果 a.y > 0,返回 1- 如果 a.y == 0 且 a.x > 0,返回 1- 否则返回 -1用于 half-plane 排序时,将向量分到“上半平面”或“下半平面”
*/
template<class T>
int sgn(const Point<T> &a) {return a.y > 0 || (a.y == 0 && a.x > 0) ? 1 : -1;
}/*判断点 p 是否在直线(有向) l 的左侧cross(l.b - l.a, p - l.a) > 0 则 p 在 l 左侧
*/
template<class T>
bool pointOnLineLeft(const Point<T> &p, const Line<T> &l) {return cross(l.b - l.a, p - l.a) >= 0;
}/*将一个二维平面上的点(向量)绕原点逆时针旋转一个角度 t(单位:弧度)
*/
template<class T>
Point<T> rotateT(Point<T> a, long double t) {long double c = cos(t);long double s = sin(t);return (Point<T>) {a.x * c - a.y * s, a.x * s + a.y * c};
}/*计算两条直线(或延长线) l1 和 l2 的交点参数:l1: 直线1,通过 l1.a, l1.bl2: 直线2,通过 l2.a, l2.b返回值:交点坐标 = l1.a + (l1.b - l1.a) * (cross(l2.b - l2.a, l1.a - l2.a) / cross(l2.b - l2.a, l1.b - l1.a))需要保证两直线不平行,否则除以 0
*/
template<class T>
Point<T> lineIntersection(const Line<T>& L1, const Line<T>& L2) {// d1 = L1.b - L1.a, d2 = L2.b - L2.aPoint<T> d1 = L1.b - L1.a;Point<T> d2 = L2.b - L2.a;// 分母:d1 × d2T den = cross(d1, d2);// 分子:(L2.a - L1.a) × d2T num = cross(L2.a - L1.a, d2);// 题目保证 den != 0,所以直接用T t = num / den;return L1.a + d1 * t;
}/*判断点 p 是否在线段 l 上(闭区间)条件:1) p 与 l.a, l.b 共线:cross(p - l.a, l.b - l.a) == 02) p.x 在 [min(l.a.x, l.b.x), max(l.a.x, l.b.x)] 范围内3) p.y 在 [min(l.a.y, l.b.y), max(l.a.y, l.b.y)] 范围内
*/
template<class T>
bool pointOnSegment(const Point<T> &p, const Line<T> &l) {return cross(p - l.a, l.b - l.a) == 0&& std::min(l.a.x, l.b.x) <= p.x && p.x <= std::max(l.a.x, l.b.x)&& std::min(l.a.y, l.b.y) <= p.y && p.y <= std::max(l.a.y, l.b.y);
}/*判断点 a 是否在简单多边形 p 内部或边界上使用射线法(or 奇偶性算法):1) 若 a 在任意一个边的线段上,则视为在多边形内,直接返回 true2) 否则,对从 a 向 +X 方向发射一条水平射线,统计与多边形边的交点个数- 若交点数为奇数,则在内部;为偶数,则在外部注意:为了避免射线与顶点共线带来的歧义,使用如下两种情况分别异或计数
*/
template<class T>
bool pointInPolygon(const Point<T> &a, const std::vector<Point<T>> &p) {int n = p.size();// 先检查是否在任意边线上for (int i = 0; i < n; i++) {if (pointOnSegment(a, Line(p[i], p[(i + 1) % n]))) {return true;}}// 射线交点计数int t = 0;for (int i = 0; i < n; i++) {auto u = p[i];auto v = p[(i + 1) % n];// 如果边 (u->v) 从左下方穿过射线if (u.x < a.x && v.x >= a.x && pointOnLineLeft(a, Line(v, u))) {t ^= 1; // 交点数 +1 (异或翻转)}// 如果边 (u->v) 从右下方穿过射线if (u.x >= a.x && v.x < a.x && pointOnLineLeft(a, Line(u, v))) {t ^= 1; // 交点数 +1 (异或翻转)}}return t == 1;
}/*计算两条线段 l1, l2 的相交情况返回值:tuple{type, p1, p2}type = 0: 不相交type = 1: 普通相交(在内部相交),交点为 p1==p2type = 2: 重叠(共线且有重叠区间),重叠区间端点为 p1, p2type = 3: 在端点相交(恰好在某个端点相交),交点为 p1==p2具体步骤:1) 先用包围盒快速排除:如果两个包围盒不重叠,返回 02) 计算两条线段方向向量叉积,若为 0,则可能共线或平行- 如果不共线,返回 0- 如果共线,则按区间重叠来判断是 2 还是 33) 非共线时,计算四个叉积 (cp1, cp2, cp3, cp4),判断是否跨立- 若不跨立,返回 0- 若跨立,计算交点 p = lineIntersection(...)- 若四个叉积都不为 0,返回 type=1- 否则必然是类型 3,在端点相交
*/
template<class T>
tuple<int, Point<T>, Point<T>> segmentIntersection(const Line<T> &l1, const Line<T> &l2) {// 包围盒检查:如果 l1 最大 x 坐标 < l2 最小 x,则不可能相交if (std::max(l1.a.x, l1.b.x) < std::min(l2.a.x, l2.b.x)) {return {0, Point<T>(), Point<T>()};}// 如果 l1 最小 x > l2 最大 x,排除if (std::min(l1.a.x, l1.b.x) > std::max(l2.a.x, l2.b.x)) {return {0, Point<T>(), Point<T>()};}// 如果 l1 最大 y < l2 最小 y,排除if (std::max(l1.a.y, l1.b.y) < std::min(l2.a.y, l2.b.y)) {return {0, Point<T>(), Point<T>()};}// 如果 l1 最小 y > l2 最大 y,排除if (std::min(l1.a.y, l1.b.y) > std::max(l2.a.y, l2.b.y)) {return {0, Point<T>(), Point<T>()};}// 计算方向向量叉积if (cross(l1.b - l1.a, l2.b - l2.a) == 0) {// 如果在同一直线上(共线)if (cross(l1.b - l1.a, l2.a - l1.a) != 0) {// 平行但不共线return {0, Point<T>(), Point<T>()};} else {// 共线:找出重叠的区间端点auto maxx1 = std::max(l1.a.x, l1.b.x);auto minx1 = std::min(l1.a.x, l1.b.x);auto maxy1 = std::max(l1.a.y, l1.b.y);auto miny1 = std::min(l1.a.y, l1.b.y);auto maxx2 = std::max(l2.a.x, l2.b.x);auto minx2 = std::min(l2.a.x, l2.b.x);auto maxy2 = std::max(l2.a.y, l2.b.y);auto miny2 = std::min(l2.a.y, l2.b.y);// 重叠区间的左下端点Point<T> p1(std::max(minx1, minx2), std::max(miny1, miny2));// 重叠区间的右上端点Point<T> p2(std::min(maxx1, maxx2), std::min(maxy1, maxy2));// 如果 p1 不在线段 l1 上,则将 y 坐标交换,使得真正的重叠端点为 p2if (!pointOnSegment(p1, l1)) {std::swap(p1.y, p2.y);}// 如果 p1 == p2,则只有一个公共端点,返回 type=3if (p1 == p2) {return {3, p1, p2};} else {// 否则返回重叠区间 (p1, p2),type = 2return {2, p1, p2};}}}// 非共线:计算四个叉积,判断跨立auto cp1 = cross(l2.a - l1.a, l2.b - l1.a);auto cp2 = cross(l2.a - l1.b, l2.b - l1.b);auto cp3 = cross(l1.a - l2.a, l1.b - l2.a);auto cp4 = cross(l1.a - l2.b, l1.b - l2.b);// 如果不跨立,则不相交if ((cp1 > 0 && cp2 > 0) || (cp1 < 0 && cp2 < 0) ||(cp3 > 0 && cp4 > 0) || (cp3 < 0 && cp4 < 0)) {return {0, Point<T>(), Point<T>()};}// 计算交点Point<T> p = lineIntersection(l1, l2);// 如果四个叉积都不为 0,则是普通相交if (cp1 != 0 && cp2 != 0 && cp3 != 0 && cp4 != 0) {return {1, p, p};} else {// 否则是端点相交return {3, p, p};}
}/*计算两条线段 l1 和 l2 之间的最短距离如果相交则距离为 0;否则取各自端点到对方线段的最短距离中的最小值
*/
template<class T>
long double distanceSS(const Line<T> &l1, const Line<T> &l2) {if (std::get<0>(segmentIntersection(l1, l2)) != 0) {// 有任何形式的相交return 0.0;}// 不相交时,计算 4 种端点-线段距离,取最小值return std::min({distancePS(l1.a, l2),distancePS(l1.b, l2),distancePS(l2.a, l1),distancePS(l2.b, l1)});
}/*判断线段 l 是否完全在简单多边形 p 内部(含边界)逻辑:1) 先判断 l 的两端点是否在多边形内部或边界上,若有一个不在,则返回 false2) 对多边形的每条边做相交检测:- 如果类型为 1(严格相交),则 l 与该边在内部相交,返回 false- 如果类型为 2(重叠),需要进一步判断重叠部分是否在合法范围内:如果重叠的顶点 v 既不等于 l.a 且不等于 l.b,并且与该顶点相关的内角为凸角,则返回 false- 如果类型为 3(端点相交),需判断交点是否在合法位置,否则返回 false3) 如果遍历完所有边仍未返回 false,则说明线段在多边形内
*/
template<class T>
bool segmentInPolygon(const Line<T> &l, const std::vector<Point<T>> &p) {int n = p.size();// 1) 两个端点必须都在多边形内部或边界上if (!pointInPolygon(l.a, p)) {return false;}if (!pointInPolygon(l.b, p)) {return false;}// 2) 遍历多边形的每一条边for (int i = 0; i < n; i++) {auto u = p[i];auto v = p[(i + 1) % n];auto w = p[(i + 2) % n];// 计算 l 与多边形边 (u->v) 的相交情况auto [t, p1, p2] = segmentIntersection(l, Line(u, v));if (t == 1) {// 严格相交(在边的内部相交),线段不在多边形内return false;}if (t == 0) {// 不相交,检验下一条边continue;}if (t == 2) {// 重叠:重叠的区间端点是 p1, p2// 如果重叠的顶点 v 既不等于 l.a 且不等于 l.b// 并且在该顶点处的内角是凸角 (cross(v-u, w-v) > 0),则线段越界if (pointOnSegment(v, l) && v != l.a && v != l.b) {if (cross(v - u, w - v) > 0) {return false;}}} else {// t == 3:端点相交// 如果交点 p1 既不等于 u 也不等于 v,则说明交在边的内部if (p1 != u && p1 != v) {// 如果 l 的端点在该边的“左侧”,说明 l 部分在外部if (pointOnLineLeft(l.a, Line(v, u)) || pointOnLineLeft(l.b, Line(v, u))) {return false;}} else if (p1 == v) {// 交点在顶点 v 处,需要判断与 v 相邻的两条边 (u-v) 和 (v-w) 的位置关系if (l.a == v) {// 交点在 l.aif (pointOnLineLeft(u, l)) {// u 在 l 的左侧if (pointOnLineLeft(w, l) && pointOnLineLeft(w, Line(u, v))) {return false;}} else {// u 在 l 的右侧或共线if (pointOnLineLeft(w, l) || pointOnLineLeft(w, Line(u, v))) {return false;}}} else if (l.b == v) {// 交点在 l.bif (pointOnLineLeft(u, Line(l.b, l.a))) {// u 相对于(l.b->l.a)的左侧if (pointOnLineLeft(w, Line(l.b, l.a)) && pointOnLineLeft(w, Line(u, v))) {return false;}} else {// u 相对于(l.b->l.a)的右侧或共线if (pointOnLineLeft(w, Line(l.b, l.a)) || pointOnLineLeft(w, Line(u, v))) {return false;}}} else {// 交点在 v,但既不等于 l.a 也不等于 l.b:// 此时 l 穿过 v,判断 u 和 w 两边是否进入多边形外部if (pointOnLineLeft(u, l)) {if (pointOnLineLeft(w, Line(l.b, l.a)) || pointOnLineLeft(w, Line(u, v))) {return false;}} else {if (pointOnLineLeft(w, l) || pointOnLineLeft(w, Line(u, v))) {return false;}}}}}}// 如果所有边都检查完毕,没有发现越界情况,则线段在多边形内return true;
}/*half-plane intersection(半平面交,求凸多边形的顶点序列)输入:一组有向直线(通常是边界线,每条线的左侧是合法区域)输出:凸多边形的顶点顺序(逆时针或顺时针,依实现而定)算法思路(常见的双端队列实现):1) 按极角排序:先比较方向向量落在哪个半平面,再比较叉积确定顺序2) 枚举排序后每条直线,用一个双端队列维护当前半平面交的边集合3) 每加一条线时,从队首和队尾弹出不满足“左侧”条件的交点4) 最后队列剩余的边会形成一个稳态的凸多边形
*/template<class T>
std::vector<int> hp(std::vector<Line<T>> lines) {// 1) 先按照极角排序std::sort(lines.begin(), lines.end(), [&](auto l1, auto l2) {// 计算方向向量 d1, d2auto d1 = l1.b - l1.a;auto d2 = l2.b - l2.a;// 如果一个在上半平面,另一个在下半平面,上半平面排前if (sgn(d1) != sgn(d2)) {return sgn(d1) == 1;}// 否则按 d1 vs d2 的叉积大者排前 (逆时针排序)return cross(d1, d2) > 0;});deque<Line<T>> ls; // 存储当前半平面交的边deque<Point<T>> ps; // 存储对应的交点for (auto l : lines) {if (ls.empty()) {// 队列为空时直接加入ls.push_back(l);continue;}// 从尾部弹出:如果最后一个交点不在新半平面的左侧,则移除while (!ps.empty() && !pointOnLineLeft(ps.back(), l)) {ps.pop_back();ls.pop_back();}// 从头部弹出:如果最前一个交点不在新半平面的左侧,则移除while (!ps.empty() && !pointOnLineLeft(ps.front(), l)) {ps.pop_front();ls.pop_front();}// 如果新直线和平行队尾直线同向,则只保留一条if (!ls.empty() && cross(l.b - l.a, ls.back().b - ls.back().a) == 0) {// 同向:保留方向更“内”的那条if (dot(l.b - l.a, ls.back().b - ls.back().a) > 0) {// 如果队尾直线的 a 不在当前新半平面左侧,则替换if (!pointOnLineLeft(ls.back().a, l)) {assert(ls.size() == 1); // 确保队列中只有这一条线ls[0] = l;}continue;}// 反向:说明无解return {};}// 计算新线与队尾直线的交点,加入交点队列ps.push_back(lineIntersection(ls.back(), l));ls.push_back(l);}ps.push_back(lineIntersection(ls[0], ls.back()));vector<int> ans;for (int i = 0; i < ls.size(); i++) {for (auto x : ls[i].id) {ans.push_back(x);}}return ans;
}using LD = long double;
using P = Point<LD>;
const LD PI = acos(-1);constexpr LD eps = 0; map<pair<int, int>, vector<int>> id;void solve() {int n;cin >> n;vector<int> v(n), b(n);for (int i = 0; i < n; i++) {cin >> b[i];}for (int i = 0; i < n; i++) {cin >> v[i];}for (int i = 0; i < n; i++) {id[{v[i], b[i]}].push_back(i + 1);}vector<Line<LD>> ls;ls.push_back({{0, 1}, {0, 0}});for (auto [b, c] : id) {Line<LD> l = {{0, b.second}, {1, b.first + b.second}};l.id = c;ls.push_back(l);}auto ans = hp(ls);ranges::sort(ans);cout << ans.size() << '\n';for (auto x : ans) {cout << x << ' ';}
}signed main() {ios::sync_with_stdio(false);cin.tie(nullptr);int t = 1;// cin >> t;while(t--){solve();}return 0;
}