Loading

[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

流程:

  1. 处理 \(pos_j=i\) 的部分。这块可以暴力统计贡献,\(O(n \sqrt n)\)
  2. \(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();
}
posted @ 2023-02-21 17:22  purplevine  阅读(9)  评论(0编辑  收藏  举报