Hướng dẫn giải của Bedao Regular Contest 20 - North Pole
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Với mỗi cầu tuyết, gọi ~f~ là vị trí mà cầu tuyết sẽ đáp xuống, ~F(x)~ là phương trình parabol được biểu diễn bởi cầu tuyết. Ta có:
$$f = s + \frac{2 v_x v_y}{G}$$
$$F(x)= \frac{v_y}{v_x} (x - s) - \frac{G}{2 v_x^2} (x - s)^2$$
Ở subtask 1, do các parabol không giao nhau hay nằm trong nhau nên tổng diện tích được bao phủ đơn giản là tổng các ~\int\limits_s^f F(x) dx~.
Trường hợp tổng quát, với mỗi cặp cầu tuyết (~i, j~), ta cần giải phương trình ~F_i(x) - F_j(x) = 0~ để tìm (các) giao điểm của parabol. Khi đó, ta xác định được các đoạn liên tục mà chỉ có 1 parabol là lớn nhất. Với mỗi đoạn, để tính diện tích ta lấy tích phân của parabol lớn nhất trên đoạn đó.
Để duy trì tập các parabol và tìm parabol lớn nhất, ta lưu 1 set các parabol. Set này sử dụng dynamic comparator so sánh giá trị các parabol tại thời điểm ~k~ với ~k~ tịnh tiến. Mỗi khi có đoạn xuất phát từ ~x_i~, ta cập nhật ~k = x_i~ rồi thêm đoạn đó vào set. Mỗi khi có 1 giao điểm tại ~x~, ta cập nhật ~k = x~ và check 2 đoạn giao nhau rồi đổi vị trí của chúng trong set. Mỗi cặp parabol chỉ giao nhau tối đa 2 điểm nên số lần shift là ~O(n^2)~.
Độ phức tạp: ~O(n^2 \cdot \log(n))~.
#include <bits/stdc++.h> // QioCas #define FOR(i,l,r) for(int i=(l); i<=(r); ++i) #define REP(i,l,r) for(int i=(l); i<(r); ++i) #define FORD(i,r,l) for(int i=(r); i>=(l); --i) #define REPD(i,r,l) for(int i=(r)-1; i>=(l); --i) #ifdef LOCAL #include </home/cas/Cp/Lib/debug.h> #else #define console(...) void(10062006) #endif using namespace std; using ll = long long; const int N = 1003; const double G = 9.8; const double EPS = 1e-9; struct Ball { public: double a() const { return B; } double b() const { return A - 2.00 * B * st; }; double c() const { return B * st * st - A * st; }; double F(double x) const { return B * (x - st) * (x - st) * (x - st) / 3.00 + A * (x - st) * (x - st) / 2.00; } double F(double l, double r) const { return F(r) - F(l); } void input() { cin >> st >> vector.first >>vector.second; A = vector.second / vector.first; B = - G / (2 * vector.first * vector.first); en = st + 2.00 * vector.first * vector.second / G; } friend std::vector<double> intersect(const Ball& u, const Ball& v) { function<std::vector<double>(std::vector<double>)> load = [&] (std::vector<double> val) { std::vector<double> res; for(const double& x : val) if(max(u.st, v.st) <= x && x <= min(u.en, v.en)) res.push_back(x); return res; }; double a = u.a() - v.a(), b = u.b() - v.b(), c = u.c() - v.c(); if(a == 0) return (b == 0 ? load({}) : load({-c / b})); double delta = b * b - 4.00 * a * c; if(delta < 0) return load({}); if(delta == 0) return load({-b / (2 * a)}); return load({(-b - sqrt(delta)) / (2 * a), (-b + sqrt(delta)) / (2 * a)}); } double st, en; pair<double, double> vector; private: double A, B; }; struct Compress : vector<double> { void load() { sort(this->begin(), this->end()); this->resize(unique(this->begin(), this->end()) - this->begin()); } int prod(const double& val) { return lower_bound(this->begin(), this->end(), val) - this->begin(); } }; int n; Ball a[N]; Compress cps; void optimize(double l, double r, int& u, int idx) { if(u == 0 || a[u].F(l, r) < a[idx].F(l, r)) u = idx; } struct Interval { #define mid ((l + r) >> 1) int IT[1000006 << 2]; void addEvent(int id, int l, int r, double s, double t, int idx) { if(cps[l - 1] >= t || cps[r] <= s) return; if(s <= cps[l - 1] && cps[r] <= t) { optimize(cps[l - 1], cps[r], IT[id], idx); return; } addEvent(id << 1, l, mid, s, t, idx); addEvent(id << 1 | 1, mid + 1, r, s, t, idx); } double dfs(int id, int l, int r, int best) { if(IT[id]) optimize(cps[l - 1], cps[r], best, IT[id]); if(l == r) { if(best == 0) return 0.00; return a[best].F(cps[l - 1], cps[r]); } return dfs(id << 1, l, mid, best) + dfs(id << 1 | 1, mid + 1, r, best); } } event; int c = 0; double at[7]; inline void add(double x) { if(c > 0 && at[c - 1] == x) return; at[c++] = x; if(c >= 2 && at[c - 2] > at[c - 1]) std::swap(at[c - 1], at[c - 2]); } bool is_used[N]; int main() { cin.tie(NULL)->sync_with_stdio(false); cin >> n; FOR(i, 1, n) a[i].input(); FOR(i, 1, n) { cps.push_back(a[i].st); cps.push_back(a[i].en); } FOR(u, 1, n) FOR(v, u + 1, n) { vector<double> its = intersect(a[u], a[v]); for(const double& x : its) cps.push_back(x); } cps.load(); int m = cps.size(); FOR(u, 1, n) FOR(v, u + 1, n) { vector<double> its = intersect(a[u], a[v]); if(!its.size()) continue; c = 0; add(a[u].st); add(a[v].st); for(const double& x : its) add(x); add(a[u].en); add(a[v].en); REP(i, 1, c) { int best = 0; REP(rot, 0, 2) { if(a[u].st <= at[i - 1] && at[i] <= a[u].en) { optimize(at[i - 1], at[i], best, u); } std::swap(u, v); } if(best) { is_used[u] = 1; event.addEvent(1, 1, m - 1, at[i - 1], at[i], best); } } } FOR(u, 1, n) { if(!is_used[u]) event.addEvent(1, 1, m - 1, a[u].st, a[u].en, u); } cout << setprecision(10) << fixed << event.dfs(1, 1, m - 1, 0) << "\n"; return 0; }
Bình luận