// O(N log(N) + M log(M)). // // Computes the upper convex hull of the dishes, then sorts the // persons CCW, and iterates over the persons and dishes together // (starting from the rightmost dish and from the person closest to // the X axis) and alternatively advances over one or the other. // There is some complication when there are multiple dishes that are // equally good for a given person, as we need to return the one with // the lowest index. #include #include #include #include #include #include #include using namespace std; using Point = array; // Cross product, the signed area of these three points. int64_t area(Point O, Point P, Point Q) { return (P[0] - O[0]) * (Q[1] - O[1]) - (P[1] - O[1]) * (Q[0] - O[0]); } struct Dish { Point p = {}; int index = -1; bool operator<(const Dish &other) const { Point pp = {p[0], -p[1]}; Point other_pp = {other.p[0], -other.p[1]}; if (pp == other_pp) { assert(index == other.index); } return pp < other_pp; } }; struct Person { Point p = {}; int index = -1; // Sort in increasing angles (break ties by index). bool operator<(const Person &other) const { int64_t a = area({0, 0}, p, other.p); if (a != 0) return a > 0; return index < other.index; } }; template vector read_input(int N, bool transpose=true) { vector read(N); for (int i = 0; i < N; i++) { read[i].index = i; } if (transpose) { for (int i = 0; i < N; i++) { for (int k = 0; k < 2; ++k) { cin >> read[i].p[k]; assert(read[i].p[k] >= 0); // Prevent overflows. assert(read[i].p[k] <= 1e8); } } } else { for (int k = 0; k < 2; ++k) { for (int i = 0; i < N; i++) { cin >> read[i].p[k]; assert(read[i].p[k] >= 0); // Prevent overflows. assert(read[i].p[k] <= 1e8); } } } return read; } // Adds a new point to 'hull', maintaining the property that 'hull' is // an upper convex hull of all added points. void extend_hull(const Dish &new_point, std::vector &hull) { while (hull.size() >= 2 && // >0 instead of >= to keep aligned points. area(hull[hull.size() - 2].p, hull.back().p, new_point.p) > 0) { hull.pop_back(); } hull.push_back(new_point); } // Computes the upper convex hull of 'inputs'. std::vector compute_hull(std::vector inputs) { sort(inputs.begin(), inputs.end()); for (int i = 0; i + 1 < inputs.size(); ++i) { assert(inputs[i].p != inputs[i + 1].p); } std::vector hull; for (const Dish &p : inputs) { extend_hull(p, hull); } return hull; } int64_t dot(const Dish &d, const Person &p) { return d.p[0] * p.p[0] + d.p[1] * p.p[1]; } int find_lowest_index(const vector &dishes, int start, const Person &person) { const int64_t cur_affinity = dot(dishes[start], person); int dish_lowest_index = start; int lowest_index = numeric_limits::max(); for (int cur = start; cur < dishes.size(); ++cur) { if (dot(dishes[cur], person) != cur_affinity) { break; } if (dishes[cur].index < lowest_index) { lowest_index = dishes[cur].index; dish_lowest_index = cur; } } return dish_lowest_index; } vector solve(vector dishes, vector persons) { vector hull = compute_hull(dishes); // We want to start the scan below from the right side of the hull. reverse(hull.begin(), hull.end()); sort(persons.begin(), persons.end()); for (int i = 0; i + 1 < persons.size(); ++i) { assert(persons[i].p != persons[i + 1].p); } vector preferred(persons.size(), -1); int dish = 0; // Indexes the points in the hull. bool got_lowest_index = false; for (const Person &person : persons) { for (; dish + 1 < hull.size(); ++dish) { int64_t cur_affinity = dot(hull[dish], person); int64_t next_affinity = dot(hull[dish + 1], person); if (cur_affinity > next_affinity) { got_lowest_index = false; break; } if (cur_affinity < next_affinity) { got_lowest_index = false; continue; } if (cur_affinity == next_affinity) { if (got_lowest_index) { break; } dish = find_lowest_index(hull, dish, person); got_lowest_index = true; break; } } preferred[person.index] = hull[dish].index; } return preferred; } int main() { int N, M; cin >> N >> M; auto dishes = read_input(N); auto persons = read_input(M); vector preferred = solve(dishes, persons); for (int pref : preferred) { cout << pref + 1 << "\n"; } }