CF601E A Museum Robbery 解题报告
最初给定 $n$ 个物品以及背包容量 $k$,有 $q$ 次操作,操作有三种:
1 v w
在背包里添加一个体积为 $v$ 价值为 $w$ 的物品2 x
删除编号为 $x$ 的物品3
查询背包总和,以 $\sum\limits_{m=1}^{k}{s(m)*p^{m-1}\ \bmod\ q}$ 的形式输出$n \leq 5000,k \leq 1000,q \leq 30000$ ,保证操作 $1$ 的个数不超过 $10000$,且至少有一个操作 $3$。
解题思路:
首先我们考虑,在背包里加入一个物品是 $\mathcal{O}(n)$ 的,但是删除一个物品并不好处理,只想到了重做一遍背包的方法,但是这样是 $\mathcal{O}(n^2)$ 的,显然会寄。
接下来观察题目的前两个操作,发现如果我们把最开始的物品看作在时间点 $0$,那么实际上所有物品可以考虑成在一个时间段上出现,这个就很符合线段树分治的套路了。
确定了算法之后,就开始考虑怎么写。首先我们记操作 $3$ 的个数有 $cnt$ 个,首先线段树建在 $[1,cnt]$ 上,每个线段树节点上考虑开一个 vector
来保存这个节点代表的区间存在的物品。我们发现这里实际上不好合并,所以我们采用标记永久化的方法。
在分治过程中,我们每访问到一个节点考虑一个额外的 $g$ 数组作背包用,将 $f$ 拷贝到 $g$ 后对于 $g$ 进行新增物品的背包操作。而在分治之后直接下传 $g$ 数组,于是我们可以写出一个伪代码框架了:
1 | void solve(int p,ll *f) { |
时间复杂度 $\mathcal{O}((n+q)\log (n+q) + q)$。注意数组如果开的较大不要使用 memset
。
代码:
1 | const int N = 1e5 + 10; |
CF601E A Museum Robbery 解题报告