Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
The constraints are tiny: . So don't invent some cursed interval tree. A direct simulation is totally fine.
Represent memory literally: an array of length , where each byte stores either for free or the block id that currently owns it.
For alloc n, scan from left to right and find the first run of consecutive zeroes. That gives the leftmost valid place automatically.
For erase x, you need to know whether is a currently alive block id. Keep an alive[id] array. If x is invalid or already erased, print ILLEGAL_ERASE_ARGUMENT; otherwise clear every cell equal to .
defragment is just stable compaction of the memory array: move every nonzero cell to the front, preserving order, then fill the rest with zeroes. Since each block is always a contiguous run of equal ids, deleting the gaps keeps block order and block contiguity intact.
This is pure simulation. Full stop. With , anything asymptotically fancy is just showing off to the wall.
So we model the memory exactly as it is described.
Let mem[i] denote byte of memory:
0 if the byte is free,Also keep:
alive[id] — whether this identifier currently refers to a live block,nextId — the next identifier to give to a successful alloc.That is enough.
alloc nWe need the leftmost free segment of length .
A left-to-right scan with a running count of consecutive free bytes does the job:
mem[i] == 0, increase the current free-run length,The first time the run length becomes , we found the earliest valid segment. Its start is
If no such segment exists, print NULL.
Otherwise:
mem[start ... start+n-1] with nextId,alive[nextId] = true,nextId,nextId.And yeah, unsuccessful allocations do not increment the id counter. That little detail is where people randomly trip and eat dirt.
erase xAn erase is legal iff is the id of a currently alive block.
So it is illegal when
In that case, print ILLEGAL_ERASE_ARGUMENT.
Otherwise:
alive[x] = false,Because each live block occupies one contiguous segment, this removes exactly that block.
defragmentAfter defragmentation, all occupied bytes must be moved as far left as possible, while preserving the order of blocks.
The easiest way to think about it: just remove all zeroes from the memory array and keep the nonzero values in the same order.
Example:
An in-place stable compaction does exactly this:
w = 0,i = 0..m-1,mem[i] != 0, write mem[w++] = mem[i],mem[w ... m-1] with 0.Why does this preserve block structure?
Because before defragmentation, memory is a sequence of contiguous block-runs separated by zero-runs. Removing all zero-runs simply concatenates the block-runs in the same order. No block gets reordered, and no block gets split. Nice and clean.
We maintain this invariant after every operation:
mem as one contiguous segment,alive[id] is true iff that block exists and has not been erased,mem[i] = 0.Now check the operations:
alloc writes one new contiguous segment into the leftmost valid gap,erase removes exactly one existing segment,defragment deletes all gaps while preserving the order of occupied bytes.So every operation matches the statement exactly.
Each command scans at most bytes, so each one costs . Hence total complexity is
Given , this is at most
which is hilariously small.
Memory usage is .
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int t, m;
cin >> t >> m;
vector<int> mem(m, 0); // 0 = free, otherwise block id
vector<int> alive(t + 5, 0); // ids are at most number of successful allocs <= t
int nextId = 1;
for (int q = 0; q < t; q++) {
string op;
cin >> op;
if (op == "alloc") {
int n;
cin >> n;
int run = 0, start = -1;
for (int i = 0; i < m; i++) {
if (mem[i] == 0) run++;
else run = 0;
if (run == n) {
start = i - n + 1;
break;
}
}
if (start == -1) {
cout << "NULL\n";
} else {
for (int i = start; i < start + n; i++) mem[i] = nextId;
alive[nextId] = 1;
cout << nextId << '\n';
nextId++;
}
} else if (op == "erase") {
ll x;
cin >> x;
if (x <= 0 || x >= nextId || !alive[(int)x]) {
cout << "ILLEGAL_ERASE_ARGUMENT\n";
} else {
alive[(int)x] = 0;
for (int i = 0; i < m; i++) {
if (mem[i] == x) mem[i] = 0;
}
}
} else { // defragment
int w = 0;
for (int i = 0; i < m; i++) {
if (mem[i] != 0) mem[w++] = mem[i];
}
while (w < m) mem[w++] = 0;
}
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int t, m;
cin >> t >> m;
vector<int> mem(m, 0); // 0 = free, otherwise block id
vector<int> alive(t + 5, 0); // ids are at most number of successful allocs <= t
int nextId = 1;
for (int q = 0; q < t; q++) {
string op;
cin >> op;
if (op == "alloc") {
int n;
cin >> n;
int run = 0, start = -1;
for (int i = 0; i < m; i++) {
if (mem[i] == 0) run++;
else run = 0;
if (run == n) {
start = i - n + 1;
break;
}
}
if (start == -1) {
cout << "NULL\n";
} else {
for (int i = start; i < start + n; i++) mem[i] = nextId;
alive[nextId] = 1;
cout << nextId << '\n';
nextId++;
}
} else if (op == "erase") {
ll x;
cin >> x;
if (x <= 0 || x >= nextId || !alive[(int)x]) {
cout << "ILLEGAL_ERASE_ARGUMENT\n";
} else {
alive[(int)x] = 0;
for (int i = 0; i < m; i++) {
if (mem[i] == x) mem[i] = 0;
}
}
} else { // defragment
int w = 0;
for (int i = 0; i < m; i++) {
if (mem[i] != 0) mem[w++] = mem[i];
}
while (w < m) mem[w++] = 0;
}
}
return 0;
}