划分 (Divide)
划分 (Divide)
题目描述
给定长度为 \(n\) 的序列 \(A\),接下来有 \(m\) 个操作需要你依次执行,操作类型有两种:
-
\(1\ x\ v\):将 \(A_x\) 改成 \(v\)。
-
\(2\ k\ B_1\ B_2\ ...\ B_k\):
如果可以将数列 \(A\) 分割成满足以下条件的区间集合,则输出 \(YES\),否则输出 \(NO\):
- 区间两两不交、区间的并是 \([1,n]\) 且没有空区间。
- 每个区间内所有数的异或结果必须在 \(B\) 序列中。
- 若可以分割,请紧接着输出一种可行的区间分割方案。
若你能保证你对于构造可行性的回答全部正确,且对于每次询问,你构造的区间个数必须小于等于 \(k^2(k+2)\),若能保证区间个数小于等于 \(k(k+2)\),即可得到全部分数,具体详见评分方式。
简单情形的做法:
一次查询有一个显然的 \(DP\) 式子:
设 \(dp_{i}\) 表示前 \(i\) 个数的最小划分数目,设 \(s_i\) 表示前缀异或和,转移如下:
其中 \(T_i\) 表示前缀异或和为 \(i\) 的位置的最小 \(dp\) 值。
一次查询时间复杂度 \(O(nk)\) 。修改可以暴力修改,不是瓶颈。
总时间复杂度就是 \(O(qnk)\) 。
只考虑查询:
\(k\le 5\)
由于 \(k\) 很小,所以单次查询应该是有一个什么 \(2^k\) 之类的时间复杂度。
由于划分是连续的且完整包含整个区间,所以每个分界点的前缀异或和一定是 \(B\) 中的若干个数异或起来的。而这些数总数不会超过 \(2^k\) 个。
这启示我们从这 \(2^k\) 个数的角度去思考问题。
不必求最小划分数:
题目没有要求划分的数量要最小。所以我们可以舍弃一下划分数量来换取时间上的高效率。
把划分看成跳跃,每次从 \(i\) 跳到 \(j\) 要求 $s_i\oplus s_j \in B $ 。
如果存在两个位置 \(i<j\) ,且 \(s_i=s_j\) 那么如果一个合法划分包含了 \([1,s_i]\) ,那么一定有一个合法区间能够包含 \([1,s_j]\) 。
也就是说只要能到 \(i\) , 那么就一定能够走到 \(j\) 。
但是走到 \(j\) 不一定能够走到 \(i\) 。
所以我们可以设 \(f_i\) 表示前缀异或和为 \(i\) 的能跳到的最前位置。
则可以类似 \(dij\) 的去转移 \(f\) 。具体地:
设 \(nxt[x][w]\) 表示位置 \(x\) 后面,前缀异或和为 \(w\) 的最前位置。
取出 \(f_i\) 最小的 \(i\) 。
那么可以枚举 \(k\in B\) 。
容易发现这样不会出现循环反复互相松弛,所以跑一遍的时间复杂度是可控的。
查询时判断是否有 \(f_{s_n}<n\) 即可。反向构造也是容易的(最后一段右端点可以直接改成 \(n\) )。
由于一共划分的区间不会超过 \(2^k\) (不会互相松弛),在 \(k\le 5\) 的情况下,\(2^k\le k(k+2)\) ,所以构造合法。
加上修改:
唯一受修改影响的就是 \(nxt\) 数组,所以只要我们能快速处理 \(nxt\) ,这道题就做完了。
单点修改,在 \(s\) 中相当于是后缀加,导致有很多个 \(nxt\) 会同时改变。
这里记 \(g[i][j]\) 表示第 \(i\) 个块内前缀异或和值 \(j\) 出现的最小位置。
考虑分块:
-
修改:整块打异或标记,散块暴力重构对应的 \(g\) 数组。
-
查询:散块暴力找,整块查询 \(g\) 数组, 要注意打标记的影响。

浙公网安备 33010602011771号