LOJ数列分块入门 6 题解 重新分块(重构)
涉及操作:
- 单点插入;
- 单调询问。
解题思路:
每 \(\sqrt n\) 次插入后,重新把数列平均分一下,重构需要的时间复杂度为 \(O(n)\),重构的次数为 \(O(\sqrt n)\),可以解决这个问题。
但是按照原作者的代码,是:如果出现某个分块的大小大于初始分块大小的 \(20\) 倍时,再重新分块。这里按照这种写法。
示例程序:
#include <bits/stdc++.h>
using namespace std;
int n, m, blo, a;
vector<int> vec[1010], tmp;
void rebuild() {
tmp.clear();
for (int i = 1; i <= m; i ++) {
for (auto x : vec[i])
tmp.push_back(x);
vec[i].clear();
}
int sz = tmp.size();
int blo2 = sqrt(sz);
for (int i = 0; i < sz; i ++)
vec[i/blo2+1].push_back(tmp[i]);
m = (sz - 1) / blo2 + 1;
}
pair<int, int> query(int p) {
int x = 1;
while (vec[x].size() < p) {
p -= vec[x].size();
x ++;
}
return { x, p-1 };
}
void add(int p, int val) {
pair<int, int> pi = query(p);
int x = pi.first, y = pi.second;
vec[x].insert(vec[x].begin() + y, val);
if (vec[x].size() > 20*blo)
rebuild();
}
int main() {
ios::sync_with_stdio(0);
cin >> n;
blo = sqrt(n);
for (int i = 1; i <= n; i ++) {
cin >> a;
vec[(i-1)/blo+1].push_back(a);
}
m = (n - 1) / blo + 1;
for (int i = 0; i < n; i ++) {
int op, l, r, c;
cin >> op >> l >> r >> c;
if (op == 0) add(l, r);
else {
pair<int, int> pi = query(r);
int x = pi.first, y = pi.second;
cout << vec[x][y] << endl;
}
}
return 0;
}