题解:P5608 [Ynoi2013] 文化课

· · 题解

思维难度不大,但卡常难度很大。

我的第一篇 Ynoi 题解?

Task

给出一串仅由数字和加法乘法的计算式。有三种操作:

  1. 区间修改数字;

  2. 区间修改运算符;

  3. 查询区间运算结果。

### Solution 一步一步来。 1. 不带修 先考虑不带任何修改怎么做。容易发现这玩意很线段树。两个节点如何合并信息呢? 如果中间运算符为 $+$ 直接两边答案加起来。如果是 $\times$,显然不能直接乘起来。考虑每个节点记录左侧的连乘长度和答案,右侧同理。实际上我们是把左边节点的右端连乘与右边的左边连乘乘起来了。 代码大概长这样: ```cpp struct Node { int l, r, llen, rlen, lmul, rmul, val; }; inline int len(Node u) { return u.r - u.l + 1; } inline Node operator + (Node u, Node v) { Node res; res.l = u.l; res.r = v.r; res.llen = u.llen == len(u) && b[u.r] ? u.llen + v.llen : u.llen; res.rlen = v.rlen == len(v) && b[u.r] ? v.rlen + u.rlen : v.rlen; res.lmul = u.llen == len(u) && b[u.r] ? mul(u.lmul, v.lmul) : u.lmul; res.rmul = v.rlen == len(v) && b[u.r] ? mul(v.rmul, u.rmul) : v.rmul; res.val = b[u.r] ? ((0ll + u.val + v.val - u.rmul - v.lmul + mul(u.rmul, v.lmul)) % P + P) % P : add(u.val, v.val); return res; } ``` 这一部分较为简单,能获得 $5$ 分。 2. 修改运算符 在线段树的每个节点上额外记录其右侧的运算符是什么。 用线段树维护区间修改即可。加一个 tag 即可实现。 注意如果你修改了运算符,那么这些区间的答案也会随之而改变。所以要重新 push_up 一轮,才能保证后面的操作不会出错。 现在是 $24$ 分。 3. 修改数值 考虑如果一段区间的数值全被修改为同一个值 $x$ 会发生什么。你会发现这段区间的答案变为了形如 $p_1x^{q_1} + p_2x^{q_2} + \cdots + p_tx^{q_t}$ 的东西。 我们可以开一个 `vector<pair<int, int> >` 强制维护这些二元组 $(p_i, q_i)$(开数组直接爆空间)。设这段区间为 $[l, r]$,发现一个式子: $$ \sum_{i = 1}^t p_i \times q_i = r - l + 1 $$ 这是显然的。由于 $q_i$ 互不相同,本质不同的二元组数量一定 $\le \sqrt{r - l + 1}$,这是好证明的。 所以暴力维护复杂度是对的。合并的时候,归并两个 vector。要判断一下中间的运算符: - 如果是 $+$,直接归并即可; - 否则,你需要合并左边区间的右侧连乘与右边区间的左侧连乘。这个可以先删除再添加,有些细节。 存下了这些二元组,区间答案的计算就很显然了。 做完了,复杂度 $O(n \sqrt n)$。 接下来进入调试和卡常环节。提供一些警示后人和卡常技巧: - **注意输入的值域是 $[1, 2^{32} - 1]$,别以为可以用 int 存,int 上界是 $2^{31} - 1$!!!!!** - 更新完运算符一定要重新 push_up 一轮答案!你可能会问:为什么不能同时更新运算符和答案?因为两者的修改区间不一样。 - 将函数写进结构体里会快很多。 - 合并两个 vector 时有的地方能 break 就 break。 - **开一个辅助 vector 用于删除 vector 中某个元素并反复 push_back 较慢。直接 [vector erase](https://en.cppreference.com/w/cpp/container/vector/erase) 即可。从中间插入的话可以 [vector insert](https://en.cppreference.com/w/cpp/container/vector/insert)。** - **一定要尽量规避大量的 push_back,可以先 resize 一个大小然后直接用下标。** - 快读快输、inline 等常规卡常方法。 祝各位卡常愉快。 大战此题将近一整天才过。看来我离滚回去上文化课不远了啊。