Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #define task "CMAX"
- #include <iostream>
- #include <cstdio>
- #include <vector>
- #include <algorithm>
- using namespace std;
- using ll = long long;
- using ld = long double;
- constexpr int N = 1e5 + 5;
- constexpr ll Inf = 1e17;
- /* Một đường thẳng */
- struct line
- {
- ll a, b;
- bool operator<(const line &x)
- {
- return a < x.a;
- }
- } s[N];
- int n, m;
- ll A[N], B[N];
- /// ---- Một tập bao trên ---- ///
- struct ConvexHullTrick
- {
- vector<int> line;
- vector<ld> point;
- /// Khởi tạo
- ConvexHullTrick(int n = 0)
- {
- // x1 = -oo
- point.emplace_back(-Inf);
- }
- /// Giao điểm hai đường thẳng được đánh chỉ số x và y
- /// Phương trình hoành độ giao điểm là:
- /// a1 * x + b1 = a2 * x + b2
- /// x = (b2-b1) / (a1-a2)
- ld ff(int x, int y)
- {
- return (ld)1.0 * (B[y] - B[x]) / (A[x] - A[y]);
- }
- /// Thao tác thêm một đường thẳng vào tập
- void Add(int i)
- {
- while ((int)line.size() > 1 || ((int)line.size() == 1 && A[line.back()] == A[i])) // Ta lặp lại cho đến khi không thể xóa được nữa
- {
- if (A[line.back()] == A[i])
- {
- /// Trong trường hợp hai đường thẳng có cùng hệ số góc ( chính là A), ta giữ lại đường thẳng có B lớn hơn
- if (B[line.back()] < B[i]) // Đường được thêm vào có B lớn hơn, phải xóa đường ở cuối đi
- {
- line.pop_back();
- if (!line.empty())
- point.pop_back();
- }
- else // Đường thẳngta muốn thêm vào không tối ưu bằng đường thẳng sẵn có trong tập
- break;
- }
- // Khi hai đường thẳng khác nhau về hệ số góc, ta mới cần phải xem xét về giao điểm
- else
- {
- // Gọi delta1, delta2, delta3 lần lượt là đường thẳng áp chót trong tập, đường thẳng xếp cuối trong tập và đường thẳng ta muốn thêm vào
- // Như anh đã nói, gọi A1 là giao delta1 và delta3, A2 là giao delta2 và delta3
- // Nếu A2 đứng trước A1 theo hoành độ, ta phải bỏ đường thẳng delta2 ra khỏi tập
- if (ff(i, line.back()) <= ff(i, line[line.size() - 2]))
- {
- line.pop_back();
- if (!line.empty())
- point.pop_back();
- }
- else
- break;
- }
- }
- // Nếu đường thẳng cuối cùng không bị xóa mà có hệ số góc giống đường thẳng ta cần thêm vào, chứng tỏ B của nó > B của đường thẳng ta cần thêm vào; dẫn đến đường này KHÔNG thể thêm vào do không tối ưu hơn các đường trong tập
- if (line.empty() || A[line.back()] != A[i])
- {
- /// Ta thêm giao điểm với đường thẳng cuối cùng (Nếu có thể)
- if (!line.empty())
- point.emplace_back(ff(line.back(), i));
- line.emplace_back(i);
- }
- }
- ll Get(ll x)
- {
- // Ta "gán" mỗi đoạn thẳng với đầu mút bên PHẢI của nó
- // Đoạn thẳng mà tại đó giá trị A * x + B đạt tối ưu chính là đoạn đầu tiên có đầu mút bên phải >= X
- int j = lower_bound(point.begin(), point.end(), x) - point.begin();
- return A[line[j - 1]] * x + B[line[j - 1]];
- }
- } f;
- void Read()
- {
- cin >> n;
- for (int i = 1; i <= n; ++i)
- cin >> s[i].a >> s[i].b;
- cin >> m;
- }
- void Solve()
- {
- // Sắp xếp các đường thẳng theo hệ số góc
- sort(s + 1, s + n + 1);
- for (int i = 1; i <= n; ++i)
- {
- A[i] = s[i].a;
- B[i] = s[i].b;
- }
- // Ta thêm lần lượt các đường thẳng vào theo hệ số góc tăng dần
- for (int i = 1; i <= n; ++i)
- f.Add(i);
- // Thực hiện truy vấn
- for (int i = 1; i <= m; ++i)
- {
- ll x;
- cin >> x;
- cout << f.Get(x) << "\n";
- }
- }
- int32_t main()
- {
- ios_base::sync_with_stdio(0);
- cin.tie(0);
- cout.tie(0);
- if (fopen(task ".INP", "r"))
- {
- freopen(task ".INP", "r", stdin);
- freopen(task ".OUT", "w", stdout);
- }
- Read();
- Solve();
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement