[CF-GYM101623-F]Factor-Free Tree

壹、题目描述

传送门 to CF

题目大意:一个点是另外一个点的祖先当且仅当这两个点的值互质。

现在给你一个树的中序遍历结果 \(a_i\)(每个点用点值表示),试判断这个中序遍历结果能否还原成一棵树,如果可以,输出一种方案,如果不行,则输出 impossible.

数据范围:\(n\le 10^6,1\le a_i\le 10^7\).

贰、题解

首先可以想到一种 \(\mathcal O(n^3)\) 的区间 \(\tt DP\) 的做法,具体如何不作解释了。

继续挖掘性质 —— 一个 \(a_i\) 能是一个区间 \([l,r]\) 的根当且仅当 \(a_i\) 和其他所有 \([l,r]\) 中的数互质,同时,我们还可以想到一个特性 —— 一个区间里面,谁来当根都一样,至于为什么,我们可以考虑先随便找到一个点,将这个点作为根,分成两个区间递归下去,然后又找根,又重新递归下去,最后一定会出现这两种情况:

  • 某个区间的所有点都被当成根一次了,就是这个区间是合法的;
  • 某个区间递归到某个层次就无法递归下去了,即这个区间是不合法的;

考虑第一种情况,这个区间是合法的,那最好。

但是如果出现了第二种情况,说明这个区间中,所有的数都两两不互质,那么这个区间无论如何都不可能被拆开,即这个区间无论如何都不可能变得合法。

还有另外一种理解:

可以类比动态树的旋转操作,如果取点 \(x\) 可以得到解,则利用旋转操作,可以在保证不改变中序遍历的情况下改变树的结构得到以另一个点 \(y\) 作为根节点的解,所以,不论取序列中哪一个和其他值互质的点作为当前的根节点,其结果都一样;

那么我们就有了正解做法:

考虑 \({\rm solve}(l,r)\) 表示构造区间 \([l,r]\) 的子树,对于这个区间,我们枚举左边的第一个数,再枚举右边的第一个数,再枚举左边的第二个数,再......直到找到合法的可以作为根的数 \(a_x\),这类似于最小值分治,然后去掉较小区间中的所有数字,递归往大区间,再加上小区间的数字,递归小区间。

考虑复杂度,每次的复杂度不超过 \(2\min(\text{minlen})\),所以这部分复杂度为 \(\mathcal O(n\log n)\),但是同时,我们还得维护一个数列能让我们快速判断一个数是否和集合中所有数互质,根据上述操作,我们维护的这个序列需要支持:

  1. 加入一个数;
  2. 删除一个数;
  3. 快速询问一个数是否和集合中其他数互质;

由于 \(a_i\le 10^7\),并且有 \(\pi(10^7)\le 10\)(其中 \(\pi(i)\) 表示 \(i\) 以内的质数个数),那么我们可以分开维护质数出现的情况,对于判断一个数,只需要判断它的每个质因数出现次数是否全为 \(1\) 即可。

但是我们可以将这个 \(\log\) 降下去,可以预处理出 \(a_i\) 和最大区间 \([l,r]\) 中所有的数互质,然后再快速判断,这样可以做到复杂度 \(\mathcal O(n\log n)\).

叁、参考代码

\(\color{red}{\text{talk is shit, but I have no code.}}\)

肆、用到の小 \(\tt trick\)

对于互质,我们可以考虑 \(\pi(i)\),然后对于每个质数分开维护。

另外就是挖掘特性,在这道题中,我们发现一个关于中序遍历的特性:

如果取点 \(x\) 可以得到解,则利用旋转操作,可以在保证不改变中序遍历的情况下改变树的结构得到以另一个点 \(y\) 作为根节点的解

posted @ 2021-03-03 22:31  Arextre  阅读(73)  评论(0编辑  收藏  举报