Play with sequence

你说的对,但是我会分块。

对于每个区块,我们维护以下信息:

  • 原始数组 dat
  • 一个指针数组 sorted,指向原始数组里的每一个元素,按照指向的值升序排序;
  • 当前区块的偏移量 delta(进行区间加时使用);
  • 一个指向 sorted 中元素的指针 ptr,表示第一个大于 minx 的元素(相当于存下有多少个元素等于最小值);
  • 当前区块的最小值 minx
  • 一个标记 flag,表示全区块内的值是否不相等。若不存在标记,则全区块的值均为 minx

对于操作 1:

  • 散块直接拍扁重构然后暴力。
  • 整块则设置 flag 为 false,表示全区块内的元素都相等。此后的维护中,不维护 sorteddeltaptr 的信息(已知全部元素相等,显然不需要这些信息)。

对于操作 2:

  • 散块拍扁重构后暴力。
  • 若标记 flag 为 false,则直接修改 minx。否则,修改 minxdelta,再二分出新的 ptr。注意新的 ptr 只可能向右移动,因此二分的区间为 [ptr,sorted.end())。

对于操作 3:

  • 散块拍扁重构后暴力。
  • 对于整块,倘若 minx 不为 0 直接返回 0。若 flag 为 false,则整个块内都是 0,返回块的大小,否则返回 ptr - sorted.begin()

以上的“拍扁重构”,指以下过程:

  1. flag 为 false,则全区块的值都是 minx,将 minx 填充至 dat,重置 delta 为 0,将 ptr 指向 sorted.begin()sorted.end()(取决于 minx 是否为 0)。
    否则,将 delta 内的值全部加回原数组中,然后重置 delta 为 0。
  2. 进行暴力操作;
  3. 重新对 sorted 排序,并重新二分 ptr 的位置。

完整代码如下:

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <iostream>
#include <numeric>
#include <optional>
#include <ranges>
#include <vector>
using namespace std;
istream& fin = cin;
ostream& fout = cout;
using ui = unsigned int;
using uli = unsigned long long int;
using li = long long int;
struct Block {
  using It = vector<li>::iterator;
  struct colorful {
    vector<li> dat;
    vector<It> sorted;
    li delta;
    decltype(sorted)::iterator ptr;
  };
  colorful dat;
  bool flag;
  li minx;
  Block(vector<li> const& a)
      : dat(colorful{a, vector<decltype(dat.dat)::iterator>(a.size()), 0, {}}),
        flag(true),
        minx(0) {
    iota(dat.sorted.begin(), dat.sorted.end(), dat.dat.begin());
    dat.ptr = dat.sorted.begin();
    reinit();
  }
  void reinit(void) {
    ranges::sort(dat.sorted, [](It x, It y) { return *x < *y; });
    dat.ptr =
        ranges::upper_bound(dat.sorted, 0, less<>(), [](It x) { return *x; });
    minx = *dat.sorted[0];
  }
  void reset(void) {
    if (!flag) {
      flag = true;
      ranges::fill(dat.dat, minx);
      dat.delta = 0;
      dat.ptr = minx == 0 ? dat.sorted.end() : dat.sorted.begin();
    } else {
      for (auto it : ranges::views::iota(dat.sorted.begin(), dat.ptr))
        **it = minx;
      for (auto it : ranges::views::iota(dat.ptr, dat.sorted.end()))
        **it += dat.delta;
      dat.delta = 0;
    }
  }
  void fill(size_t l, size_t r, int x) {
    if (l != 0 || r != dat.dat.size()) {
      reset();
      for (size_t i = l; i < r; ++i) dat.dat[i] = x;
      reinit();
      return;
    }
    flag = false;
    minx = x;
  }
  void add(size_t l, size_t r, int x) {
    if (l != 0 || r != dat.dat.size()) {
      reset();
      for (size_t i = l; i < r; ++i) dat.dat[i] = max(dat.dat[i] + x, 0ll);
      reinit();
      return;
    }
    minx = max(minx + x, 0ll);
    if (!flag) return;
    dat.delta += x;
    dat.ptr = ranges::upper_bound(dat.ptr, dat.sorted.end(), 0, less<>(),
                                  [&](It v) { return *v + dat.delta; });
  }
  size_t count(size_t l, size_t r) {
    if (l != 0 || r != dat.dat.size()) {
      reset();
      return ranges::count(dat.dat.begin() + l, dat.dat.begin() + r, 0);
    }
    if (minx > 0) return 0;
    if (!flag) return dat.sorted.size();
    return dat.ptr - dat.sorted.begin();
  }
};
int main(void) {
  ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
  size_t n, m;
  fin >> n >> m;
  size_t len = 450;
  vector<Block> blks;
  for (size_t i = 0; i < n; i += len) {
    size_t j = min(i + len, n);
    vector<li> a(j - i);
    for (li& x : a) fin >> x;
    blks.emplace_back(a);
  }
  while (m--) {
    char op;
    size_t l, r;
    fin >> op >> l >> r;
    --l;
    size_t bl = l / len, br = (r + len - 1) / len;
    if (op == '1') {
      int c;
      fin >> c;
      for (size_t i = bl; i < br; ++i) {
        size_t cl = max(l, i * len) - i * len,
               cr = min(r, (i + 1) * len) - i * len;
        blks[i].fill(cl, cr, c);
      }
    }
    if (op == '2') {
      int c;
      fin >> c;
      for (size_t i = bl; i < br; ++i) {
        size_t cl = max(l, i * len) - i * len,
               cr = min(r, (i + 1) * len) - i * len;
        blks[i].add(cl, cr, c);
      }
    }
    if (op == '3') {
      size_t ans = 0;
      for (size_t i = bl; i < br; ++i) {
        size_t cl = max(l, i * len) - i * len,
               cr = min(r, (i + 1) * len) - i * len;
        ans += blks[i].count(cl, cr);
      }
      fout << ans << '\n';
    }
  }
  return 0;
}

这份代码可以取得 63 分的好成绩。还需要优化。

首先,进行散块操作后,观察 sorted 数组,可以发现:将被修改过的数和未被修改过的数分成两个数组,则这两个数组都是升序排列的。我们可以直接使用 的序列合并操作取代 的排序操作。

其次,在区间加操作的二分步骤前检查 minx,倘若 minx > 0,则说明全部元素都只进行可普通的加法,没有与 0 取 ,则等于 minx 的元素数量不变,无需重新二分 ptr。此优化可以将不少次 的二分操作优化成

使用以上两种优化即可通过全部测试数据。最终代码如下所示。

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <iostream>
#include <numeric>
#include <optional>
#include <ranges>
#include <vector>
using namespace std;
istream& fin = cin;
ostream& fout = cout;
using ui = unsigned int;
using uli = unsigned long long int;
using li = long long int;
using It = vector<li>::iterator;
constexpr auto UF = [](It x) { return *x; };
struct Block {
  struct colorful {
    vector<li> dat;
    vector<It> sorted;
    li delta;
    decltype(sorted)::iterator ptr;
  };
  colorful dat;
  bool flag;
  li minx;
  Block(vector<li> const& a)
      : dat(colorful{a, vector<decltype(dat.dat)::iterator>(a.size()), 0, {}}),
        flag(true),
        minx(0) {
    iota(dat.sorted.begin(), dat.sorted.end(), dat.dat.begin());
    dat.ptr = dat.sorted.begin();
    reinit();
  }
  void reinit(void) {
    ranges::sort(dat.sorted, [](It x, It y) { return *x < *y; });
    dat.ptr =
        ranges::upper_bound(dat.sorted, 0, less<>(), [](It x) { return *x; });
    minx = *dat.sorted[0];
  }
  void reset(void) {
    if (!flag) {
      flag = true;
      ranges::fill(dat.dat, minx);
      dat.delta = 0;
      dat.ptr = minx == 0 ? dat.sorted.end() : dat.sorted.begin();
    } else {
      for (auto it : ranges::views::iota(dat.sorted.begin(), dat.ptr))
        **it = minx;
      for (auto it : ranges::views::iota(dat.ptr, dat.sorted.end()))
        **it += dat.delta;
      dat.delta = 0;
    }
  }
  void remerge(size_t l, size_t r) {
    vector<It> a, b;
    for (auto it : dat.sorted)
      (l <= it - dat.dat.begin() && it - dat.dat.begin() < r ? b : a)
          .emplace_back(it);
    ranges::merge(a, b, dat.sorted.begin(), less<>(), UF, UF);
    dat.ptr = ranges::upper_bound(dat.sorted, 0, less<>(), UF);
    minx = *dat.sorted.front();
  }
  void fill(size_t l, size_t r, int x) {
    if (l != 0 || r != dat.dat.size()) {
      reset();
      for (size_t i = l; i < r; ++i) dat.dat[i] = x;
      remerge(l, r);
      return;
    }
    flag = false;
    minx = x;
  }
  void add(size_t l, size_t r, int x) {
    if (l != 0 || r != dat.dat.size()) {
      reset();
      for (size_t i = l; i < r; ++i) dat.dat[i] = max(dat.dat[i] + x, 0ll);
      remerge(l, r);
      return;
    }
    minx = max(minx + x, 0ll);
    if (!flag) return;
    dat.delta += x;
    if (minx > 0) return;
    dat.ptr = ranges::upper_bound(dat.ptr, dat.sorted.end(), 0, less<>(),
                                  [&](It v) { return *v + dat.delta; });
  }
  size_t count(size_t l, size_t r) {
    if (l != 0 || r != dat.dat.size()) {
      reset();
      return ranges::count(dat.dat.begin() + l, dat.dat.begin() + r, 0);
    }
    if (minx > 0) return 0;
    if (!flag) return dat.sorted.size();
    return dat.ptr - dat.sorted.begin();
  }
};
int main(void) {
  ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
  size_t n, m;
  fin >> n >> m;
  size_t len = 450;
  vector<Block> blks;
  for (size_t i = 0; i < n; i += len) {
    size_t j = min(i + len, n);
    vector<li> a(j - i);
    for (li& x : a) fin >> x;
    blks.emplace_back(a);
  }
  while (m--) {
    char op;
    size_t l, r;
    fin >> op >> l >> r;
    --l;
    size_t bl = l / len, br = (r + len - 1) / len;
    if (op == '1') {
      int c;
      fin >> c;
      for (size_t i = bl; i < br; ++i) {
        size_t cl = max(l, i * len) - i * len,
               cr = min(r, (i + 1) * len) - i * len;
        blks[i].fill(cl, cr, c);
      }
    }
    if (op == '2') {
      int c;
      fin >> c;
      for (size_t i = bl; i < br; ++i) {
        size_t cl = max(l, i * len) - i * len,
               cr = min(r, (i + 1) * len) - i * len;
        blks[i].add(cl, cr, c);
      }
    }
    if (op == '3') {
      size_t ans = 0;
      for (size_t i = bl; i < br; ++i) {
        size_t cl = max(l, i * len) - i * len,
               cr = min(r, (i + 1) * len) - i * len;
        ans += blks[i].count(cl, cr);
      }
      fout << ans << '\n';
    }
  }
  return 0;
}
posted @ 2025-01-26 16:06  MrPython  阅读(5)  评论(0)    收藏  举报  来源