2021.11.2 模拟赛题解
咦?鸽子 tzc 竟然补模拟赛的题解了?奇迹奇迹(好像它自从 CSP 以后就没有写过模拟赛的题解呢(
T1
看到 RBS 计数问题,可能的套路不过两种,要么把左括号当作 \(1\),右括号当作 \(-1\),然后做一遍前缀和,但是应用到此题上似乎不太可行,另一种思路则是用栈记录未匹配过的括号,每次加入一段左括号时直接将这段左括号压入栈,加入一段右括号时就贪心地用这段右括号去匹配左括号。但是计算贡献时不能简简单单地拿左右括号相消的个数计算新产生的 RBS 个数,因为有可能出现 \(()()\) 的情况,此时当我们用第四个括号(也就是最后一个右括号)去匹配栈中仅有的一个左括号时,新产生的合法括号序列不是简简单单的 \(()\) 一种,还会新产生 \(()()\) 这个括号序列。因此对于一段左括号,我们额外记录一个 \(pre\),表示有多少个合法括号序列以这段左括号左边的右括号结尾,例如在括号序列 \((())()()\) 中,当我们压入第四个左括号(倒数第二个字符)时,它的 \(pre\) 就是 \(2\)(因为 \(s[5…6],s[1..6]\) 都是合法括号序列)。这样当我们用一段右括号去匹配左括号时,如果这段左括号能够完全被匹配,那么答案需额外加上这段左括号的 \(pre\),我们再额外维护一个变量表示有多少个 RBS 以当前字符结尾,我们称之为 \(cnt\)。当我们加入一段左括号时,我们就令这段左括号的 \(pre\) 等于 \(cnt\),并令 \(cnt\) 等于 \(0\),当我们匹配一段右括号时,如果最后一段与这段右括号匹配的左括号恰好被完全消去,那么令 \(cnt\) 加 \(1\),否则如果右括号没有被完全匹配完(即 \(())\) 的情况),那么令 \(cnt\) 等于 \(0\),否则令 \(cnt\) 等于 \(1\)。时间复杂度 \(\Theta(n)\)。
T2
看到“不能出现 xxx”这类的字眼,我们可以很自然地想到容斥原理,具体来说,我们考虑选出一个子序列,再钦定一些位置集合 \(S\),满足 \(\forall x\in S\),都有 \(x\) 与 \(x\) 的后面三个位置的异或和等于 \(s\),答案加上 \((-1)^{|S|}\)。考虑如何求之,我们注意到 \(a\) 序列中元素两两不同,也就是说不可能出现序列中相邻两个元素同时被钦定的情况,否则,假设我们选择的子序列为 \(a_{i_1},a_{i_2},\cdots,a_{i_k}\),且我们同时钦定了序列中两个相邻元素 \(a_{i_p},a_{i_{p+1}}\),那么可以推得 \(a_{i_p}\oplus a_{i_{p+1}}\oplus a_{i_{p+2}}\oplus a_{i_{p+3}}=s\),以及 \(a_{i_{p+1}}\oplus a_{i_{p+2}}\oplus a_{i_{p+3}}\oplus a_{i_{p+4}}=s\),两个式子异或一下可得 \(a_{i_p}=a_{i_{p+4}}\),与题目条件矛盾!
有了这个性质之后我们就设 \(dp_{i,j}\) 表示钦定了 \(\ge i\) 的位置是否被选择以及是否 \(\in S\),且 \(i\in S\),序列中下一个被钦定的位置为 \(j\) 的所有方案的容斥系数之和,再设 \(f_i=\sum\limits_{j}dp_{i,j}\)。转移就枚举下一个被钦定的位置 \(k\),有三种情况:
- \((j,k)\) 中没有数属于我们选出的子序列,那显然 \(k\) 下一个元素必须等于 \(s\oplus a_i\oplus a_j\oplus a_k\),找出它,设它对应的位置为 \(p\),那么有 \(dp_{i,j}\leftarrow -dp_{k,p}\)。
- \((j,k)\) 中恰有一个数属于我们选出的子序列,那么选出的元素也必须等于 \(s\oplus a_i\oplus a_j\oplus a_k\),判断一下它是否在区间 \((j,k)\) 中,然后 \(dp_{i,j}\leftarrow -f_k\)。
- \((j,k)\) 中有至少两个数属于我们选出的子序列,那么我们枚举前两个元素 \(p,q\),显然必须有 \(a_i\oplus a_j\oplus a_p\oplus a_q=s\),而 \(q\) 后面的元素显然选不选皆可,方案数 \(2^{k-1-q}\),故 \(dp_{i,j}\leftarrow -f_k·2^{k-1-q}\)。
当然也有 \(i\) 是第一个被钦定的元素的情况,这时我们枚举 \(j\) 后面两个元素 \(k,l(k<l)\),满足 \(a_i\oplus a_j\oplus a_k\oplus a_l=s\),那么 \(l\) 后面的元素可以随便选择,方案数 \(2^{n-l}\)。
直接转移复杂度是 \(n^3\) 的,不过我们发现这个 DP 可以用前缀和的方式优化,具体来说,我们记录一下几个量:
- \(s1_{i,j}=\sum\limits_{i\le k<p}dp_{k,p}[a_k\oplus a_p=j]\)
- \(s2_{i,j}=\sum\limits_{i\le k<p}f_p[a_k\oplus a_p=j]\)
- \(s3_{i,j}=\sum\limits_{i\le k<p}2^{n-p}[a_k\oplus a_p=j]\)
- \(s4_{i,j}=\sum\limits_{i\le p<q}\sum\limits_{k=q+1}^nf_k·2^{k-1-q}[a_p\oplus a_q=j]\)
不难发现四个数组恰好对应了四种情况,因此只要能快速维护以上四个数组就可以实现 \(\mathcal O(1)\) 更新 DP 值,而以上四个数组显然可以用前缀和的方式求出,这样总复杂度就降到了 \(\Theta(n^2)\)。
注:可能有人不太能理解 \(s4\) 这种含两个 \(\sum\) 的东西怎么快速计算,具体来说我们就设 \(sf_k=f_k·2^{k-1}\),然后实时维护 \(sf\) 的后缀和 \(ssf\),在计算完所有 \(dp_{p,j}\) 以后我们枚举 \(q\),然后更新 \(s4_{p,a_p\oplus a_q}\leftarrow s4_{p,a_p\oplus a_q}+ssf_{q+1}·2^{-q}\),这样像 \(s4\) 这种含两个 \(\sum\) 的式子也可快速维护。
T3
首先这个模型长得很像最短路,因此我们不妨往最短路的方向思考,我们先建立一个最朴素的图论:对于每个 \(i\) 即 \(j\in[L,R]\),连边 \((id+j)\bmod n\to i\),然后以 \(0\) 为源点跑一遍 BFS,时间复杂度 \(n^2\),无法通过。
考虑优化,我们考虑用一个堆优化存边的技巧(不知道这个技巧?不妨去做做上上场 div1 的 B,也就是 CF1601B),我们 BFS 到一个节点 \(x\) 时,假设当前用了 \(t\) 的时间,我们用 \(x\) 去松弛 \([x-R,x-L]\) 中的所有点,即对于所有 \(y\in[x-R,x-L]\) 令 \(dis_y=t\),并将所有满足 \(zd\equiv y\pmod{n}\) 的 \(z\) 压入队列并记其时间戳为 \(t+1\),由于一个点最多被松弛一遍,因此松弛完所有符合要求的点 \(y\) 之后就可以将 \(y\) 删去,这样复杂度瓶颈就在于查找 \([x-R,x-L]\) 中还没有被删去的点,这个可以使用 set
维护,时间复杂度 \(\Theta(n\log n)\),相较于之前 \(n^2\) 的解法快了许多,但还是无法通过,如果实现较为优秀则可以拿到 \(84\) 分的好成绩。
考虑进一步优化,不难发现到这一步为止,问题可以抽象为如下的模型:你有若干个点,你要支持快速查询某个位置以后下一个在区间中的点并将该点从区间中删除。考虑用并查集维护这个过程,我们将每个点看作一条连接 \(i\) 与 \(i+1\) 的边,初始这些边都是断开的,当我们删去一个点时,就将 \(i\) 与 \(i+1\) 之间的边连上,然后查询一个连通块中编号最大的点即可知道下一个未被删去的点,使用并查集维护这个过程可以做到 \(n\alpha(n)\)。
T4
首先一个很明显的事实是,由于 \(\sigma\) 是积性函数,而 \(n\) 是 square-free number,因此对于任意 \(m\mid n\) 以及任意一种填写 A 中格子的方案,都有 A 中格子的 \(\sigma\) 之积等于 \(\sigma\),而显然,对于固定的 \(m\),填写 A 中格子使 A 中格子的乘积等于 \(m\) 的方案数等于 \(n_a^{\omega(m)}\),这个用乘法原理很好证明。
比较棘手的一点是 \(f(x)=f_0+f_1x+f_2x^2\) 不一定是积性函数,因此 B 中填写的数的 \(f(x)\) 之积不一定等于 \(f(m)\),不过看到这种低阶多项式的乘积,我们不禁往拆位的方向思考,具体来说我们将乘法里的 \(\prod(f_0+f_1x+f_2x^2)\) 中每一位拆开来,我们考虑枚举选出的项中有多少个 \(x^0\),有多少 \(x^1\),以及多少 \(x^2\),不妨设之为 \(A,B,C\),因此我们可以写出一个非常朴素的式子:
其中 \(P\) 为输入的质因子集合。后面的组合数表示选择 \(A\) 个位置,其括号中的三项中选择 \(f_0\),再选择 \(B\) 个位置,其括号中的三项中选择 \(f_1x\),\(\prod\) 表示考虑每个质因子的贡献,如果它填在一个被钦定选择 \(f_0\) 的位置中,贡献为 \(1\),如果它填在一个被钦定选择 \(f_1x\) 的位置中,贡献为 \(p_i\),否则贡献为 \(p_i^2\)。
这样暴力计算显然复杂度过高,考虑对其进行一些变形,我们考虑每个质因子 \(x\) 的贡献,如果它不在 \(S\) 中贡献就是 \(1\),否则贡献为 \((x+1)·n_a·(A+p_iB+p_i^2C)\),因此式子可以改写为:
考虑上式的组合意义:有 \(n_b\) 个位置,现在你要选择若干个位置作为 1 号位,若干个位置作为 2 号位,剩余位置为 3 号位,每选出一个 1 号位会产生 \(f_0\) 的贡献,每选出一个 2 号位会产生 \(f_1\) 的贡献,每选出一个 3 号位会产生 \(f_2\) 的贡献,还额外给出 \(k\) 个数 \(p_1,p_2,p_3,\cdots,p_k\),对于每个 \(p_i\),你可以选择把它丢进任意一个位置上,也可以选择不丢,如果选择丢那会产生 \((p_i+1)·n_a\) 的基本贡献,如果你丢的是二号位,那么会产生 \(p_i\) 的额外贡献,如果你丢的是二号位,那么会产生 \(p_i^2\) 的额外贡献,定义一种选法的贡献为所有贡献的乘积。求所有选法的乘积之和。
可以发现,\(n_b\) 数据范围很大,高达 \(10^{18}\),但是 \(k\) 数据范围很小,这意味着大部分位置是没有任何数选择丢到这个位置上的,我们假设这样的位置有 \(t\) 个,那么不难发现,对于这些位置,所有选法的贡献之和就是 \(\sum\limits_{A+B+C=t}f_0^Af_1^Bf_2^C\dbinom{t}{A,B,C}=(f_0+f_1+f_2)^t\),因此我们只用考虑那些有数选择的位置即可,我们设 \(dp_{i,x,y,z}\) 表示当前决策了前 \(i\) 个数选择的位置,目前有 \(x\) 个被选择的 1 号位,\(y\) 个被选择的 2 号位,\(z\) 个被选择的 3 号位的贡献之和,转移就分以下七种情况转移即可:
- \(p_i\) 不选择
- \(p_i\) 选择丢进一个已经被选择的一号位
- \(p_i\) 选择丢进一个已经被选择的二号位
- \(p_i\) 选择丢进一个已经被选择的三号位
- \(p_i\) 选择丢进一个未被选择的一号位
- \(p_i\) 选择丢进一个未被选择的二号位
- \(p_i\) 选择丢进一个未被选择的三号位
直接做个背包即可,时间复杂度 \(\Theta(k^4+k^3\log n_b)\)。