ABC344G 题解

题意

给定 \(N\) 个二维平面上的点 \((X_i, Y_i)\)\(Q\) 组询问,每组询问给出一条直线 \(Y = A_iX + B_i\),问有多少个点在直线上方(或者在直线上)。也就是询问有多少个 \((X_i, Y_i)\),满足 \(Y_i \ge A_j \times X_i + B_j\)

题解

首先这个式子是 \(A \times X + B \le Y\),移项得 \(-A \times X + Y \ge B\)。假设每组询问的 \(A\) 相等,那么把 \((X, Y)\)\(-A \times X + Y\) 排序,然后二分答案即可。而对于 \(A\) 不同的情况,沿用刚才的方法,考虑维护这个排序后的序列(记为 \(B\))。

这个序列显然不能在线维护,考虑离线。离线后对 \(Q\) 个询问按 \(A\) 从大到小排序,思考 \(A\) 变小后,序列会发生什么变化。也就是对于两个点对 \((X_i, Y_i), (X_j, Y_j)\) 且在序列 \(B\) 中有 \(i < j\),原本存在关系 \(-A_1X_i + Y_i \le -A_1X_j + Y_j\),移项得 \(-A_1(X_i - X_j) + (Y_i - Y_j) \le 0\)。在 \(A_1\) 变成 \(A_2\) ( \(A_2 \le A_1\) ) 后,如果 \(i\) 被放到 \(j\) 的后面,就存在 \(-A_2(X_i - X_j) + (Y_i - Y_j) \ge 0\),孤立 \(A_2\) 之后得到 \(A_2 \le \frac{Y_i - Y_j}{X_i - X_j}\),而 \(\frac{Y_i - Y_j}{X_i - X_j}\) 其实就是 \((X_i, Y_i)\)\((X_j, Y_j)\) 两点所连直线的斜率。

然后这道题就可以做了。首先将询问按 \(A = +\infty\) 时的 \(-AX_i + Y_i\) 排序,其实就是将 \(X_i\) 按从大到小排序,\(X_i\) 相等时将 \(Y\) 从小到大。接着开一个小根堆存序列 \(B\) 中相邻两点的斜率。一个一个询问往后扫,每次扫到一个新询问,看一下小根堆存的斜率里面有没有可以被更新顺序的。然后对 \(B_i\) 二分就行了。复杂度 \(O(Q \log N + N^2 \log N)\),这题时限 10s 可以过。

code:

#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
using namespace std;
using i64 = long long;
using ld = double;
const int N = 5E3 + 5, Q = 1E7 + 5, MOD = INT_MAX;
const ld eps = 1E-6;
int n, q, posi[N], pposi[N];
i64 g[Q << 2], ra, rb;
struct pos {
  i64 x, y;
  bool operator < (const pos &w) const {
    return x == w.x ? y < w.y : x < w.x;
  }
} a[N];
struct que {
  i64 a; i64 b;
  bool operator < (const que &w) const {
    return a < w.a;
  }
} b[Q];
struct node {
  i64 X, Y; 
  int x;
  bool operator < (const node &w) const {
    i64 l = X * w.Y;
    i64 r = Y * w.X;
    if (l < r) return false;
    if (l > r) return true;
    return x > w.x;
  }
  node (i64 a, i64 b, int c) {X = a, Y = b, x = c;}
} ;
priority_queue <node> pq;
void push(int x) {
  if (x <= 1 || x >= n + 1) return ;
  if (a[x - 1] < a[x]) pq.emplace(node(a[x].y - a[x - 1].y, a[x].x - a[x - 1].x, x));
}
signed main(void) {
  ios :: sync_with_stdio(false);
  cin.tie(nullptr); cout.tie(nullptr);
  cin >> n;
  for (int i = 1; i <= n; ++i) cin >> a[i].x >> a[i].y;
  cin >> q >> g[0] >> ra >> rb;
  for (int i = 1; i <= 3 * q; ++i)
    g[i] = g[i - 1] * 48271 % MOD;
  for (int i = 1; i <= q; ++i) {
    b[i].a = -ra + g[3 * i - 2] % (2 * ra + 1);
    b[i].b = -rb + (g[3 * i - 1] * MOD % (2 * rb + 1) + g[3 * i]) % (2 * rb + 1);
  }
  sort(b + 1, b + 1 + q); sort(a + 1, a + 1 + n);
  for (int i = 2; i <= n; ++i) push(i);
  i64 sum = 0;
  for (int i = 1; i <= q; ++i) {
    int A = b[i].a;
    while (!pq.empty()) {
      i64 X = pq.top().X, Y = pq.top().Y; int x = pq.top().x;
      if (X != a[x].y - a[x - 1].y || Y != a[x].x - a[x - 1].x) {pq.pop(); continue;}
      i64 l = X, r = 1LL * Y * A;
      if (l >= r) break;
      pq.pop(); swap(a[x - 1], a[x]); 
      push(x - 1); push(x + 1); 
    }
    int l = 1, r = n + 1;
    while (l < r) {
      int mid = (l + r) >> 1;
      auto check = [&](int k) {
        return a[k].y >= 1LL * a[k].x * A + b[i].b;
      } ;
      if (check(mid)) r = mid;
      else l = mid + 1;
    }
    sum += n - l + 1;
  }
  cout << sum << '\n';
}
posted @ 2024-03-10 17:27  CTHOOH  阅读(50)  评论(0)    收藏  举报