做了那么久的水题,来道有难度的
问题
题意和永无乡类似,多了一个回退操作,支持回到第 $x$ 次操作后的状态, $n, m \le 10^5$ ,时间 $500ms$ ,空间 $20MB$
先不管时空,看操作
回退
先看比永无乡多的这个操作,说是回退,但其实又和一般回退不同,因为它是“回到第 $x$ 次操作后的状态”,不只是这期间的加边要撤销,这期间的“回到……”也要撤销掉
那咋办呢?考虑建树,具体地,建出 $0$ 号点为根,对于第 $i$ 次操作(它的编号为 $i$ ),如果它是加边,就让它的父亲为 $i - 1$ ;如果是回到 $x$ ,就让它的父亲为 $x$ ;如果是询问,其实可以打标记的,但是最坏情况下对时空都没有优化,所以还是直接让它的父亲为 $i - 1$ 即可
这样只要dfs遍历一遍操作树,根据节点信息操作,就可以得到答案,时空消耗均为 $O(n)$ ,可以接受
操作
现在来看如何根据节点信息操作,首先这是dfs,所以我们的操作必须支持撤销,且能够在线维护加边(联通)和查询第 $k$ 小
可撤销的查询第 $k$ 小可以考虑可持久化平衡树,但sm的空间( $20MB$ )不允许,考虑分块,对值域进行分块,记得离散化,单次 $O(\sqrt{n})$
加边很明显用并查集,不打路径压缩,按秩合并单次询问可以做到 $O(\log n)$
时空
现在来看时空限制,明显时空都是 $O(n\sqrt{n})$ ,一般的题也就过去了,但这个出题人不一般
时间 $500ms$ 没问题,但 $20MB$ 的空间只允许 $n \log n$ ,shit
尝试卡块数,设为 $B$ ,发现:
- $B \le 25$ 时, $96pts$ TLE
- $25 < B \le 27$ , $92pts$ TLE + WA(不知道为啥WA)
- $B > 27$ , $88pts$ MLE
这就是由乃oi吗,i了i了
但是,我们发现 short
可以存 $2^{15} - 1$ ,大概 $3 \times 10^4$ ,而 $n / B$ 在 $B > 10$ 的情况下不大于 short
所以可以开成 short
,空间变为 $O(\frac{n B}{2})$ ,计算出 $20MB = 20 * 1024 * 1024B$ , $n \le 10^5$ ,不难发现 $20 * 1024 * 1024 / 2 / n$ , $B$ 大概可以开 $100$ ,但别的数组也要空间呀,所以 $B$ 开个 $50$ 差不多
调试
调试时遇到一些问题,莫名其妙输出 $0$ ,样例太水了,手打了 $makedate$ 和脚本对拍:
1 | //make_date.cpp |
1 | #check.bat |
调出来发现是 ask
函数里面 for (int i = (y - 1) * blk + 1, t = y * blk; i <= t && k; ++i)
写成了 for (int i = (y - 1) * blk + 1, t = y * num; i <= t && k; ++i)
导致询问大小只有 $1$ 的联通块时输出 $0$
代码
1 |
|