Advertisement
Dang_Quan_10_Tin

CMAX

May 15th, 2022
942
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 4.48 KB | None | 0 0
  1. #define task "CMAX"
  2. #include <iostream>
  3. #include <cstdio>
  4. #include <vector>
  5. #include <algorithm>
  6.  
  7. using namespace std;
  8.  
  9. using ll = long long;
  10. using ld = long double;
  11.  
  12. constexpr int N = 1e5 + 5;
  13. constexpr ll Inf = 1e17;
  14.  
  15. /* Một đường thẳng */
  16. struct line
  17. {
  18.     ll a, b;
  19.     bool operator<(const line &x)
  20.     {
  21.         return a < x.a;
  22.     }
  23. } s[N];
  24.  
  25. int n, m;
  26. ll A[N], B[N];
  27.  
  28. /// ---- Một tập bao trên ---- ///
  29. struct ConvexHullTrick
  30. {
  31.     vector<int> line;
  32.     vector<ld> point;
  33.  
  34.     /// Khởi tạo
  35.     ConvexHullTrick(int n = 0)
  36.     {
  37.         // x1 = -oo
  38.         point.emplace_back(-Inf);
  39.     }
  40.  
  41.     /// Giao điểm hai đường thẳng được đánh chỉ số x và y
  42.     /// Phương trình hoành độ giao điểm là:
  43.     /// a1 * x + b1 = a2 * x + b2
  44.     /// x = (b2-b1) / (a1-a2)
  45.  
  46.     ld ff(int x, int y)
  47.     {
  48.         return (ld)1.0 * (B[y] - B[x]) / (A[x] - A[y]);
  49.     }
  50.  
  51.     /// Thao tác thêm một đường thẳng vào tập
  52.     void Add(int i)
  53.     {
  54.         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
  55.         {
  56.            
  57.             if (A[line.back()] == A[i])
  58.             {
  59.                 /// 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
  60.  
  61.                 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
  62.                 {
  63.                     line.pop_back();
  64.                     if (!line.empty())
  65.                         point.pop_back();
  66.                 }
  67.                 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
  68.                     break;
  69.             }
  70.  
  71.             // 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
  72.             else
  73.             {
  74.                 // 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
  75.                 // Như anh đã nói, gọi A1 là giao delta1 và delta3, A2 là giao delta2 và delta3
  76.                 // Nếu A2 đứng trước A1 theo hoành độ, ta phải bỏ đường thẳng delta2 ra khỏi tập
  77.  
  78.                 if (ff(i, line.back()) <= ff(i, line[line.size() - 2]))
  79.                 {
  80.                     line.pop_back();
  81.                     if (!line.empty())
  82.                         point.pop_back();
  83.                 }
  84.                 else
  85.                     break;
  86.             }
  87.         }
  88.  
  89.         // 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
  90.  
  91.         if (line.empty() || A[line.back()] != A[i])
  92.         {
  93.             /// Ta thêm giao điểm với đường thẳng cuối cùng (Nếu có thể)
  94.  
  95.             if (!line.empty())
  96.                 point.emplace_back(ff(line.back(), i));
  97.  
  98.             line.emplace_back(i);
  99.         }
  100.     }
  101.  
  102.     ll Get(ll x)
  103.     {
  104.         // Ta "gán" mỗi đoạn thẳng với đầu mút bên PHẢI của nó
  105.         // Đ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
  106.  
  107.         int j = lower_bound(point.begin(), point.end(), x) - point.begin();
  108.         return A[line[j - 1]] * x + B[line[j - 1]];
  109.     }
  110. } f;
  111.  
  112. void Read()
  113. {
  114.     cin >> n;
  115.  
  116.     for (int i = 1; i <= n; ++i)
  117.         cin >> s[i].a >> s[i].b;
  118.  
  119.     cin >> m;
  120. }
  121.  
  122. void Solve()
  123. {
  124.     // Sắp xếp các đường thẳng theo hệ số góc
  125.     sort(s + 1, s + n + 1);
  126.  
  127.     for (int i = 1; i <= n; ++i)
  128.     {
  129.         A[i] = s[i].a;
  130.         B[i] = s[i].b;
  131.     }
  132.  
  133.     // Ta thêm lần lượt các đường thẳng vào theo hệ số góc tăng dần
  134.     for (int i = 1; i <= n; ++i)
  135.         f.Add(i);
  136.     // Thực hiện truy vấn
  137.     for (int i = 1; i <= m; ++i)
  138.     {
  139.         ll x;
  140.         cin >> x;
  141.  
  142.         cout << f.Get(x) << "\n";
  143.     }
  144. }
  145.  
  146. int32_t main()
  147. {
  148.     ios_base::sync_with_stdio(0);
  149.     cin.tie(0);
  150.     cout.tie(0);
  151.  
  152.     if (fopen(task ".INP", "r"))
  153.     {
  154.         freopen(task ".INP", "r", stdin);
  155.         freopen(task ".OUT", "w", stdout);
  156.     }
  157.  
  158.     Read();
  159.     Solve();
  160. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement