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;
}

浙公网安备 33010602011771号