ABC283E 题解
前言
很简单的一道题,强行在英语课的时候想到做法。
存储方式与其他题解稍有不同。本题解着重讲是怎么想到这个做法的。
思考过程
首先考虑暴力。用 \(n\) 个数组(或者 vector 等方式)记录不同版本,每次要保存或更改成版本,直接把 vector 粘贴过来当前列表即可,时间复杂度 \(O(q^2)\)。
我们考虑是什么地方导致花销巨大:是存档操作。
我们发现,所有存档之间有一定联系。尝试用一个 \(pos_i\) 去记录第 \(i\) 个存档的位置。假设当前是在 \(cur\) 位置,备份直接 \(pos_i = cur\) 即可,然后直接做。
但是这样有一个明显的缺点:载入完后啥都做不了了啊!
正解
考虑下面的解决方法。
事实上,想到用树的方式存储,这题就做完了:
- 对于 \(\texttt{ADD}\) 操作,只需要新建节点,并往当前的 \(cur\) 后面接儿子。然后跳过去。
- 对于 \(\texttt{DEL}\) 操作,我们可以在 \(\texttt{ADD}\) 的时候记录一下,每一个节点的前一个节点是多少。直接跳 \(cur = fa_{cur}\)。
- 对于 \(\texttt{SAVE}\) 操作,直接 \(pos_i = cur\)。
- 对于 \(\texttt{LOAD}\) 操作,直接 \(cur = pos_i\)。
每次输出 \(cur\) 的值即可。注意 \(pos_x\) 中的 \(x\) 可能会很大。可以离散化或者用更简单的 map 存储。
代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <map>
using namespace std;
const int N = 5e5 + 5;
map <int, int> pos;
int fa[N], val[N];
vector <int> son[N]; //存储儿子
int main()
{
ios::sync_with_stdio(false);
int T, cur = 0, idx = 0; //根节点是0
cin >> T;
while (T--)
{
string op; int x;
cin >> op;
if (op != "DELETE") cin >> x;
if (op == "ADD") son[cur].push_back(++idx), val[idx] = x, fa[idx] = cur, cur = idx;
else if (op == "DELETE") cur = fa[cur];
else if (op == "SAVE") pos[x] = cur;
else if (op == "LOAD") cur = pos[x];
printf("%d ", val[cur] == 0 ? -1 : val[cur]); //注意空的时候输出 -1 而不是 0
}
return 0;
}
希望能帮助到大家!