CF1635F 笔记

好题啊。

题意

给定 \(n\) 个二元组 \((x_i, w_i)\),保证 \(x\) 升序。有 \(m\) 个询问 \([l, r]\),对于每个询问求出:

\[\min\limits_{l \le i < j \le r}(x_j - x_i) \cdot (w_i + w_j) \]

题解

一个精妙的结论:

  • \(L_i\) 表示 \(i\) 左边第一个满足 \(w_j \le w_i\)\(j\)\(R_i\) 表示 \(i\) 右边第一个满足 \(w_j \le w_i\)\(j\)
  • 一个询问的答案的最优 \((i, j)\) 一定在 \((x, R_x)\) 或者 \((L_x, x)\) 处取到。

证明:反证法。假设最优处在 \((i, j)\)\(i \neq L_j \land R_i \neq j\)。那么一定有 \((i, L_j)\) 或者 \((R_i, j)\) 更优,原因如下:
因为 \(w_{L_j} \le w_j\)\((w_i + w_{L_j}) \le (w_i + w_j)\) 并且还有 \((x_{L_j} - x_i) < (x_j - x_i)\)。故乘积更小。
所以 \((i, j)\) 有更优处,与假设矛盾。证毕

于是有了这个结论之后,不难用树状数组维护答案,复杂度 \(O(n \log n)\)

这道题的结论同样启示我们,对于这种 RMQ 问题(或者取全局最小值的问题),都要尝试思考一下最值在哪些位置取得,这些位置的性质是什么等等。

代码:

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 3E5 + 5;
int n, q, x[N], w[N]; i64 Ans[N];
int L[N], R[N]; vector <int> vr[N];
vector <pair <int, int>> v[N];
i64 calc(int i, int j) {
  if (i > j) swap(i, j);
  return 1LL * (x[j] - x[i]) * (w[i] + w[j]);
}
struct bit {
  i64 c[N];
  void insert(int x, i64 val) {
    for (; x; x -= x & -x)
      c[x] = min(c[x], val);
  }
  i64 query(int x) {
    i64 r = 4E18;
    for (; x <= n; x += x & -x)
      r = min(c[x], r);
    return r;
  }
  void init() {for (int i = 0; i <= n; ++i) c[i] = 4E18;}
} t;
signed main(void) {
  ios :: sync_with_stdio(false);
  cin.tie(0); cout.tie(0);
  cin >> n >> q;
  for (int i = 1; i <= n; ++i) cin >> x[i] >> w[i];
  stack <int> st;
  for (int i = n; i >= 1; --i) {
    while (!st.empty() && w[st.top()] >= w[i]) {L[st.top()] = i; st.pop();}
    st.emplace(i);
  } while (!st.empty()) L[st.top()] = -1, st.pop();
  for (int i = 1; i <= n; ++i) {
    while (!st.empty() && w[st.top()] >= w[i]) {R[st.top()] = i; st.pop();}
    st.emplace(i);
  } while (!st.empty()) R[st.top()] = -1, st.pop();
  for (int i = 1; i <= q; ++i) {
    int l, r; cin >> l >> r;
    v[r].emplace_back(l, i);
  } t.init();
  for (int i = 1; i <= n; ++i) if (~R[i]) vr[R[i]].emplace_back(i);
  for (int i = 1; i <= n; ++i) {
    for (auto l : vr[i]) 
      t.insert(l, calc(l, i));
    if (~L[i]) t.insert(L[i], calc(L[i], i));
    for (auto [l, id] : v[i]) {
      Ans[id] = t.query(l);
    }
  }
  for (int i = 1; i <= q; ++i) cout << Ans[i] << '\n';
  return 0;
}
posted @ 2024-03-09 15:51  CTHOOH  阅读(26)  评论(0)    收藏  举报