一道令人不得不佩服的二合一题目
(然而“二合一”的两个部分我一个都没弄出来)
# 题面
点击展开/折叠题面
著名炼金术士威廉正在完成他秘密计划的最后一步。
威廉一开始有一个已经固定的长度为 $n$ 的序列 $a_i=i,i\in[0,n-1]$ 一个自己设定的一个长度为 $n$ 的序列 $b_i$。
按照古书上的记载,威廉现在可以对这两个序列施展魔法,施展一次魔法可以使这个序列向右循环位移一次,即序列中最后一个位置上的数字移到第一个数字的前面,其余的数字的下标依次加一。如果威廉对某个序列 $z$ 施展了 $i$ 次魔法,那么我们将得到的结果序列设为 $Magic(z,i)$。
现在,威廉需要用若干个相同的 $a$ 序列和 $b$ 序列得到他最终需要的魔法阵。
具体来说,威廉需要先准备好两个矩阵 $A$ 和 $B$,其中矩阵 $A$ 的第 $i,i\in[0,n-1]$ 行是 $Magic(a,i)$,同理,矩阵 $B$ 的第 $i,i\in[0,n-1]$ 行是 $Magic(b,i)$。
那么威廉最终要得到的魔法阵就是 $C=A\times B$。
当然,这些魔法对威廉来说实在是太简单了,因此他想对他的学生——你提出 $q$ 个询问或操作来检验一下自己的教学成果:
0 x0 y0 x1 y1
:查询魔法阵中左上角为$(x_0,y_0)$,右下角为$(x_1,y_1)$的子矩形的 $C_{i,j}^t$ 之和模 $n$ 的结果,注意 $t$ 是一开始给定的常数。1 i k p
:对魔法阵 $C$ 中的第 $i$ 行从左到右依次加上 $Magic(a,p)$ 中的每个数字的 $k$ 倍。2 j k p
:对魔法阵 $C$ 中的第 $j$ 列从上到下依次加上 $Magic(a,p)$ 中的每个数字的 $k$ 倍。3 x0 y0 x1 y1
:查询魔法阵中左上角为$(x_0,y_0)$ ,右下角为$(x_1,y_1)$的子矩形的$C_{i,j}$之和模 $n$ 的结果。
威廉当然知道这有些难为反应有些迟钝的你,因此他的第一类询问只会在一开始提出,一旦提出了其他询问或操作,之后就不会再出现。
你能成功回答威廉提出的所有问题吗?
# 解析
看到“第一类询问只会在一开始提出”就知道这道题大概是二合一……
在分类处理两个部分前,需要证明大小两个性质。
第一个小性质
$$\{C_{i,0},C_{i,1},\dots,C_{i,n-1}\}=Magic(\{C_{0,0},C_{0,1},\dots,C_{0,n-1}\},i)$$
证明略,展开即可。
第二个重要性质
设 $\sum b=S$,则 $\{C_{i,0},C_{i,1},\dots,C_{i,n-1}\}$ 在模 $n$ 意义下是公差为 $S$ 的等差数列。
$$ \begin{aligned} C_{i,j}&=\sum_kA_{i,k}B_{k,j}\\ C_{i,j+1}&=\sum_kA_{i,k}B_{k,j+1}\\ &=\sum_kA_{i,k}B_{k-1,j}\\ &=\sum_kA_{i,k+1}B_{k,j} \end{aligned} $$
因为 $A_{i,k+1}=A_{i,k}+1$,所以 $C_{i,j+1}-C_{i,j}=S$。
(考试的时候两个都发现了,但是第二个没发现重点)
这样就可以先 $O(n)$ 算出 $C_{0,0}$,然后再利用性质二每次加 $S$ 得到 $C_{0,i}$;因为性质一,就相当于求到了整个 $C$ 矩阵。
- 询问1
根据性质一,我们可以推得矩阵 $C$ 的主对角线方向上的数字是相等的。
把从左下角的主对角线到右上角的主对角线依次标号为 $1\sim 2n-1$,如下图:
然后看询问框出的一个矩形:
于是套路维护前后缀三角形后缀(黄色、红色区域)和普通前缀和(蓝色区域)。
就是写代码的时候注意下标。
- 询问2
发现两种操作都是在行或列上加一个等差数列,而且原来矩阵 $C$ 的每一行/列也是等差数列。
于是可以维护每一行的等差数列的首项以及公差;和每一列的修改新增的等差数列的首项及公差(一定是修改的等差数列,避免计算重复)。
因为等差数列对应项求和得到的数列也是等差数列,所以可以首项、公差分别求和,用树状数组就可以了。
详见代码,思路清晰,比较好写。
# 源代码
1 | /*Lucky_Glass*/ |
THE END
Thanks for reading!
> Linked 樱花绽放时-网易云