
A block list looks roughly like this...
A block list is simply a linked list in which each node points to an array.
We divide the original array of length n into nodes, with an array of size corresponding to each node.
Thus we define the following structure; the code is shown below.
Here sqn denotes sqrt(n), namely , and pb denotes push_back, which adds an element to this node.
Implementation
struct node {
node* nxt;
int size;
char d[(sqn << 1) + 5];
node() { size = 0, nxt = NULL, memset(d, 0, sizeof(d)); }
void pb(char c) { d[size++] = c; }
};
A block list should support at least splitting, insertion, and lookup.
What is splitting? Splitting divides one node into two smaller node objects, keeping the size of every node close to (otherwise it may degenerate into an ordinary array). Perform a split when a node exceeds elements.
How is splitting performed? First create a new node, then copy the last values of the node being split into the new node, delete those last values from the original node (size--), and finally insert the new node after the original node.
Every operation on a block list has complexity .
There is one more issue. As elements are inserted or deleted, changes, and so does . The block size would therefore change as well; must we maintain the block size every time?
In fact, no. We can set to a fixed value. For example, if the limit in the problem is , set to a constant of size and do not change it.
list<vector<char>> orz_list;
rope in libstdc++
Import
The rope in libstdc++ also serves the purpose of a block list. It uses a persistent balanced tree and supports random access as well as insertion and deletion.
Since rope is not actually implemented as a block list, its time complexity is not the same as that of a block list; instead, it is the complexity of a persistent balanced tree, namely .
It can be imported as follows:
#include <ext/rope>
using namespace __gnu_cxx;
Library functions beginning with double underscores
In OI, whether library functions beginning with double underscores could be used was once uncertain. The Supplementary explanation on restrictions for programming languages used in NOI-series activities released by CCF in 2021 states that “library functions or macros beginning with an underscore may be used, except for those whose use is explicitly prohibited.” Therefore, rope can currently be used normally in OI.
Basic operations
| Operation | Effect |
|---|---|
rope<int> a | Initialize rope (much like containers such as vector) |
a.push_back(x) | Add element x to the end of a |
a.insert(pos, x) | Add element x at position pos of a |
a.erase(pos, x) | Delete x elements from position pos of a |
a.at(x) or a[x] | Access the x-th element of a |
a.length() or a.size() | Get the size of a |
Examples
Solution: This is a simple template problem. The code is as follows:
#include <cctype>
#include <cstring>
#include <iostream>
using namespace std;
constexpr int sqn = 1e3;
struct node { // define the block list
node* nxt;
int size;
char d[(sqn << 1) + 5];
node() { size = 0, nxt = NULL; }
void pb(char c) { d[size++] = c; }
}* head = NULL;
char inits[(int)1e6 + 5];
int llen, q;
void readch(char& ch) { // read a character
do cin >> ch;
while (!isalpha(ch));
}
void check(node* p) { // check; remember to split
if (p->size >= (sqn << 1)) {
node* q = new node;
for (int i = sqn; i < p->size; i++) q->pb(p->d[i]);
p->size = sqn, q->nxt = p->nxt, p->nxt = q;
}
}
void insert(char c, int pos) { // insert an element; use the linked list model
node* p = head;
int tot, cnt;
if (pos > llen++) {
while (p->nxt != NULL) p = p->nxt;
p->pb(c), check(p);
return;
}
for (tot = head->size; p != NULL && tot < pos; p = p->nxt, tot += p->size);
tot -= p->size, cnt = pos - tot - 1;
for (int i = p->size - 1; i >= cnt; i--) p->d[i + 1] = p->d[i];
p->d[cnt] = c, p->size++;
check(p);
}
char query(int pos) { // query
node* p;
int tot;
for (p = head, tot = head->size; p != NULL && tot < pos;
p = p->nxt, tot += p->size);
tot -= p->size;
return p->d[pos - tot - 1];
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> inits >> q;
llen = strlen(inits);
node* p = new node;
head = p;
for (int i = 0; i < llen; i++) {
if (i % sqn == 0 && i) p->nxt = new node, p = p->nxt;
p->pb(inits[i]);
}
char a;
int k;
while (q--) {
readch(a);
if (a == 'Q')
cin >> k, cout << query(k) << '\n';
else
readch(a), cin >> k, insert(a, k);
}
return 0;
}