Play with sequence
你说的对,但是我会分块。
对于每个区块,我们维护以下信息:
- 原始数组
dat
; - 一个指针数组
sorted
,指向原始数组里的每一个元素,按照指向的值升序排序; - 当前区块的偏移量
delta
(进行区间加时使用); - 一个指向
sorted
中元素的指针ptr
,表示第一个大于minx
的元素(相当于存下有多少个元素等于最小值); - 当前区块的最小值
minx
; - 一个标记
flag
,表示全区块内的值是否不相等。若不存在标记,则全区块的值均为minx
。
对于操作 1:
- 散块直接拍扁重构然后暴力。
- 整块则设置
flag
为 false,表示全区块内的元素都相等。此后的维护中,不维护sorted
、delta
、ptr
的信息(已知全部元素相等,显然不需要这些信息)。
对于操作 2:
- 散块拍扁重构后暴力。
- 若标记
flag
为 false,则直接修改minx
。否则,修改minx
、delta
,再二分出新的ptr
。注意新的ptr
只可能向右移动,因此二分的区间为 [ptr
,sorted.end()
)。
对于操作 3:
- 散块拍扁重构后暴力。
- 对于整块,倘若 minx 不为 0 直接返回 0。若
flag
为 false,则整个块内都是 0,返回块的大小,否则返回ptr - sorted.begin()
。
以上的“拍扁重构”,指以下过程:
- 若
flag
为 false,则全区块的值都是minx
,将 minx 填充至 dat,重置delta
为 0,将ptr
指向sorted.begin()
或sorted.end()
(取决于minx
是否为 0)。
否则,将delta
内的值全部加回原数组中,然后重置delta
为 0。 - 进行暴力操作;
- 重新对 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;
}