验题人题解:P17769 「LDOI R2」数树术 / Vocaloid

· · 题解

验题选手,做了快 3 小时才会,还咕了很久才补代码。

solution

值域很小啊,不妨考虑从因数分解入手。

先考虑考虑序列的情况。

对于一个序列 b_1,b_2,\ldots,b_k,令

x_0=1,\qquad x_i=\varphi(x_{i-1}b_i)

我们需要求出 x_k。

\varphi(x)=\prod_{p^e\parallel x}p^{e-1}(p-1)

也就是说,每次求欧拉函数,相当于对于每个不同的质因子 p,删掉一个 p,再乘上一个 p-1。

直接维护每个质因子的指数似乎不太方便,因为删掉一个 p 后,还会产生一些更小的质因子。

考虑寻找一些更好的定义。

定义一个函数 F_p(x),满足:

F_p(1)=0,\qquad F_p(xy)=F_p(x)+F_p(y)

对于一个质数 q,定义:

F_p(q)=F_p(q-1)+[p=q].

在这种定义下,对于质数 q,把它替换成 q-1,在这些新的坐标中,只有第 q 个坐标会减少 1。

于是:

F_p(\varphi(x))=F_p(x)-[p\mid x]

原本的欧拉函数操作,现在就转化为只需要考虑一个质数是否出现过。

令:

h_p=F_p(x),\qquad w_p(a)=F_p(a)

那么加入一个点权 a 后:

h'_p=h_p+w_p(a)-[p\mid xa]

这时每个坐标的变化只可能是 w_p(a)-1 或 w_p(a)。

我们记 d_p(a)=w_p(a)-1。

那么无论如何,一次操作至少会让 h_p 增加 d_p(a)。

现在的问题就是如何快速判断 p\mid xa。

我们发现,虽然答案可能很大,但是中间结果出现的质因子其实是有限的。

令 A=\max a_i,考虑集合:

\mathcal P=\bigcup_{1\le a\le A}\operatorname{Prime}(\varphi(a))

其中 \operatorname{Prime}(x) 表示 x 的所有不同质因子。

而:

\varphi(ax)=\varphi(a)\cdot x \prod_{\substack{p\mid x\\p\nmid a}}\frac{p-1}{p}

如果 x 的所有质因子都属于 \mathcal P,那么 \varphi(ax) 的所有质因子也属于 \mathcal P。

这是因为对于 p\in\mathcal P,p-1=\varphi(p) 的质因子也都属于 \mathcal P。

所以我们只需要维护集合 \mathcal P 中的质数即可。

由于 A\le1000,预处理可以发现:

|\mathcal P|\le50.

也就是说,每个状态实际上就是一个至多 50 维的数组。

不过还有一个问题,我们维护的是 F_p(x),并不是普通的质因子指数。

设 e_p=v_p(x)。

根据前面定义的变换,可以推出:

e_p=h_p-\sum_{q\in\mathcal P}v_p(q-1)h_q

所以我们可以在 O(|\mathcal P|+R) 的时间内,从所有 h_p 还原出普通质因子指数,其中 R 是所有非零的 v_p(q-1) 的数量。

这样我们就可以判断 p\mid xa,从而在 O(|\mathcal P|+R) 的时间内模拟一次欧拉函数转移。

我们再观察一下欧拉函数的性质。

一个质数 q 被替换成 q-1 后,只会产生比 q 小的质因子。

所以我们可以建立一张有向图:

q\to p\iff p\mid q-1

因为一定有 p<q,所以这是一张 DAG。

令 H 表示这张 DAG 的最长路径边数,显然 H\le 5。

这其实是本题最关键的性质。

它说明一个质因子经过欧拉函数操作后,所产生的影响沿着质数依赖关系不断向更小的质数传递,而依赖链的长度是非常短的。

重新考虑序列 b_1,b_2,\ldots,b_k。

令 h_{p,i} 表示处理完前 i 个数后,答案的第 p 个坐标。

再定义 g_{p,i},表示只取序列末尾至多 H+1 个数:

b_{\max(1,i-H)},\ldots,b_i

从初始值 1 开始计算,得到的答案的第 p 个坐标。

那么有一个非常重要的结论:

h_{p,i}= \max\left( h_{p,i-1}+d_p(b_i),\ g_{p,i} \right)

也就是说,我们完全不用再考虑之前那个质数是否出现过了,只需要知道上一个坐标,以及末尾一个长度至多为 H+1 的窗口。

我们考虑把同一段后缀,分别从两个不同的初始状态开始计算。

设得到的结果分别为 X,Y,其中 Y\mid X。由于欧拉函数保持整除关系,整个过程中始终有这个性质。

考虑商 X/Y。

因为 F_p 是完全可加的,所以:

F_p(X)-F_p(Y)=F_p(X/Y)\ge0.

假设我们发现第 p 个坐标还有差异,但 X/Y 中已经没有质因子 p。

那么这部分差异一定是由某个更大的质数 q 贡献的,并且可以沿着

q\to\cdots\to p

这条依赖链追溯。

如果经过一次欧拉函数操作后,这种差异仍然没有转化成实际的质因子 p,同时较小的初始状态也没有贡献 p,我们就必须继续向前追溯到一个更大的质数。

不断这样追溯,就会得到一条越来越长的质数依赖链。

但依赖链最多只有 H 条边。

因此,在连续处理了 H 个数之后,如果较大的结果中没有质因子 p,那么两个结果在第 p 个坐标上的值必然相同。

有了这个性质,来看第 i 次转移。

如果 p\mid x_{i-1}b_i,那么这次一定会减去 1,所以:

h_{p,i}=h_{p,i-1}+d_p(b_i)

而且完整序列的结果是对应后缀结果的倍数,因此这个值不会小于 g_{p,i}。

如果 p\nmid x_{i-1}b_i,那么前面 H 步已经足够消除第 p 个坐标上可能残留的额外影响。

此时完整序列和末尾窗口在这个坐标上的结果相同:

h_{p,i}=g_{p,i}

又因为每次转移最多减去 1,此时有

h_{p,i}\ge h_{p,i-1}+d_p(b_i)

于是两种情况合起来,就得到了:

h_{p,i}= \max(h_{p,i-1}+d_p(b_i),g_{p,i})

特别注意,这并不是说只需要末尾 6 个点就能确定答案。前面所有点的贡献仍然保留在 h_{p,i-1} 中。

我们发现,对于每个质数 p,转移已经完全独立了。

把下标 p 省略,转移就是:

h_i=\max(h_{i-1}+d_i,g_i)

展开一下:

\begin{aligned} h_1&=\max(h_0+d_1,g_1),\\ h_2&=\max(h_0+d_1+d_2,g_1+d_2,g_2) \end{aligned}

继续展开,可以发现:

h_k= \max\left( h_0+\sum_{i=1}^kd_i,\ \max_{1\le i\le k} \left(g_i+\sum_{j=i+1}^kd_j\right) \right)

所以问题就变成了维护一个路径上的前缀和,以及一些经过前缀和调整后的最大值。

这种东西显然就可以考虑树链剖分加线段树了。

我们把树以 1 为根,定义:

S_p(u)=\sum_{x\in1\to u}d_p(a_x)

其中 S_p(0)=0。

由于路径有方向,我们需要分别考虑从上往下走和从下往上走。

对于任意一个节点 u,如果它至少有 H 个祖先,那么我们可以预处理两种长度为 H+1 的窗口。

第一种是从祖先向下走到 u,定义:

D_p(u)=F_p\left( \operatorname{val}(fa^H(u)\to u) \right)

这里 \operatorname{val} 表示按题意计算一条有向路径的权值。

假设整个路径最终到达 v,那么这个窗口对答案的贡献就是:

D_p(u)+S_p(v)-S_p(u) =S_p(v)+\left(D_p(u)-S_p(u)\right)

对于一次询问,S_p(v) 是固定的,所以我们只需要维护:

T^\downarrow_p(u)=D_p(u)-S_p(u)

然后在线段树上查询对应路径的最大值即可。

第二种是从 u 向上走到它的第 H 个祖先,定义:

U_p(u)=F_p\left( \operatorname{val}(u\to fa^H(u)) \right).

假设当前向上走的这段路径终点是 z,那么这个窗口的贡献为:

U_p(u)+S_p(fa^{H+1}(u))-S_p(fa(z))

同样,对于一次询问,最后的 S_p(fa(z)) 是固定的。

所以我们维护:

T^\uparrow_p(u)=U_p(u)+S_p(fa^{H+1}(u))

这样我们就得到了两棵线段树,一棵维护向上窗口的信息,一棵维护向下窗口的信息。

线段树每个节点维护一个至多 50 维的数组,合并时每个坐标分别取最大值就行。

而且这两个线段树都是静态的,不需要考虑修改。

对于询问 u\to v,令:

z=\operatorname{lca}(u,v).

我们把路径拆成:

u\to z,\qquad z\to v.

首先考虑 u\to z。

我们直接暴力计算前 H 个点的状态。

之后所有新产生的长度为 H+1 的窗口,都是从下往上走的,可以使用 T^\uparrow 进行路径最大值查询。

配合前缀和,就可以一次性计算出走到 z 时的所有坐标。

接下来考虑 z\to v,注意这里不再重复处理 z。

我们再暴力模拟前 H 个点。

这样做是为了处理所有可能跨过 LCA 的窗口。

处理完以后,剩余的窗口都只会从上往下走,可以直接使用 T^\downarrow 进行路径最大值查询。

于是,我们就得到了整条路径最终的 h_p。

最后还原出普通的质因子指数:

e_p=h_p-\sum_{q\in\mathcal P}v_p(q-1)h_q.

答案就是:

ans=\prod_{p\in\mathcal P}p^{e_p}\bmod998244353

预处理每个质数的二进制幂,最后直接快速幂即可。

如果 \mathcal P 为空,那么所有答案都是 1,直接输出即可。

时间复杂度:O(n\log n+m\log^2n)。