[ds 记录] CF765F Souvenirs
调了好久发现块数目出错。
分块基操:查询时需要整块内部贡献、散块对整块、整块对整块。分别处理。
然后有了一种分块:处理 \(pre_{i,j}, nxt_{i,j}\) 分别表示 \(j\) 到第 \(i\) 块块头(\(pos_j \geq i\))与 \(j\) 到第 \(i\) 块块尾(\(pos_j \leq i\))内部的贡献。附加条件是快速处理散块对散块的贡献。
学习至 solution to CF765F - Utilokasteinn。
流程:
- 处理 \(pos_j=i\) 的部分。这块可以暴力统计贡献,\(O(n \sqrt n)\)。
- \(pre_{i,j} = pre_{i, j-1} + pre_{i+1, j} + ans(i, j)\),其中 \(ans_{i,j}\) 是 \(i\) 整块对 \(j\) 的答案。这里的 \(+\) 意为结合。
\(ans_{i, j}\) 采取整块式的处理,即统一为所有同块的 \(j\) 求出答案。
看一下这样做的性质。
首先它是在区间选点对。暴力统计 \(pos_j=i\) 的一部分需要这个来只枚举一对点。
然后至少一个区间对一个区间的贡献要好求些。这道题用了双指针,均摊 \(O(1)\)。
一个区间对一个区间很多时候好处在于能按另一个维度的信息排序。这题就是按数值大小排序。
最后信息要好结合。\(\min, +\) 都是好结合的信息。
所以区间逆序对(P5046)可以一个做法。怎么一紫一黑呢。
#include <bits/stdc++.h>
using namespace std;
const int M = 2e5 + 5, N = 325;
void cmin(int &x, int y) { x = min(x, y); }
int n, m, a[M];
pair<int, int> b[M];
int len, block, L[N], R[N], pos[M];
int pre[N][M],
nxt[N][M]; // j 到 i 块首(pos_j <= i)、j 到 i 块头(pos_j >= i)
int ans[M];
void calc(int p, int q) { // q 块所有点对 p 块算 ans
int pos = L[p];
for (int i = L[q]; i <= R[q]; i++) {
int x = b[i].first;
while (pos < R[p] && b[pos + 1].first <= x) ++pos;
int y = b[pos].first, z = pos + 1 == R[p] + 1 ? 2e9 : b[pos + 1].first;
ans[b[i].second] = min(abs(x - y), abs(x - z));
}
}
void init() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = {a[i], i};
len = sqrt(n);
block = (n - 1) / len + 1;
for (int i = 1; i <= block; i++) {
L[i] = R[i - 1] + 1, R[i] = min(i * len, n);
for (int j = L[i]; j <= R[i]; j++) pos[j] = i;
}
for (int i = 1; i <= block; i++) {
sort(b + L[i], b + R[i] + 1);
pre[i][L[i]] = 2e9;
for (int j = L[i] + 1; j <= R[i]; j++) {
int mn = 2e9;
for (int k = L[i]; k < j; k++) cmin(mn, abs(a[j] - a[k]));
pre[i][j] = min(pre[i][j - 1], mn);
}
nxt[i][R[i]] = 2e9;
for (int j = R[i] - 1; j >= L[i]; j--) {
int mn = 2e9;
for (int k = R[i]; k > j; k--) cmin(mn, abs(a[j] - a[k]));
nxt[i][j] = min(nxt[i][j + 1], mn);
}
}
for (int l = 2; l <= block; l++) {
for (int i = 1; i + l - 1 <= block; i++) {
int k = i + l - 1;
calc(i, k);
for (int j = L[k]; j <= R[k]; j++)
pre[i][j] = min(pre[i][j - 1], min(pre[i + 1][j], ans[j]));
}
for (int i = block; i - l + 1 >= 1; i--) {
int k = i - l + 1;
calc(i, k);
for (int j = R[k]; j >= L[k]; j--)
nxt[i][j] = min(nxt[i][j + 1], min(nxt[i - 1][j], ans[j]));
}
}
}
int tmp[N], tmp1[N], tmp2[N];
int query(int l, int r) {
int p = pos[l], q = pos[r], res = 2e9;
if (p == q) {
int tot = 0;
for (int i = L[p]; i <= R[p]; i++)
if(l <= b[i].second && b[i].second <= r) tmp[++tot] = b[i].first;
for (int i = 1; i < tot; i++)
cmin(res, tmp[i + 1] - tmp[i]);
return res;
}
int tot1 = 0, tot2 = 0;
for (int i = L[p]; i <= R[p]; i++)
if (l <= b[i].second && b[i].second <= r) tmp1[++tot1] = b[i].first;
for (int i = L[q]; i <= R[q]; i++)
if (l <= b[i].second && b[i].second <= r) tmp2[++tot2] = b[i].first;
merge(tmp1 + 1, tmp1 + tot1 + 1, tmp2 + 1, tmp2 + tot2 + 1, tmp + 1);
for(int i = 1; i < tot1 + tot2; i++)
cmin(res, tmp[i + 1] - tmp[i]);
return min(res, min(nxt[q - 1][l], pre[p + 1][r]));
}
void solve() {
int m; scanf("%d", &m);
while (m--) {
int l, r; scanf("%d %d", &l, &r);
printf("%d\n", query(l, r));
}
}
int main() {
init(), solve();
}
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/17141778.html