如何优雅地生成一棵树

本文由某位不愿透露姓名的出题人在造树的数据快造疯了的情况下写作,所以可能没什么价值。
下文默认生成以 1 为根的有根树。

“随机树”

\(i\) 的父节点为 \([1,i)\) 中的随机数。
优点:树高是 \(O(\log n)\) 级别;非常容易写出生成器。
缺点:树高是 \(O(\log n)\) 级别,可以被复杂度为 \(\sum dep\)\(\sum siz\) 的暴力通过;保证了拓扑序为 \(1\sim n\),可能会没卡掉一些错误写法。
解决缺点的方法:换一个生成方式;节点重标号。

随机树

随机一个 prufer 序列,再将其还原成一棵树。
优点:树高是 \(O(\sqrt n)\) 级别,不太容易被暴力通过;最为均匀随机的生成方法,树不会有什么特殊性质。
缺点:树高仍然不算太高,在部分情况下还是会被暴力通过;生成器相对难写。

如果你不想考虑到要让选手更加方便实现部分分,那为了加强数据强度,记得重标号。
优点:容易实现;树高很大。
缺点:形态过于简单,且过于知名,容易被特判解决;有些错误算法在链上非常快。

菊花(星形图)

如果你不想考虑到要让选手更加方便实现部分分,那为了加强数据强度,记得重标号。
优点:容易实现;根节点度数很大。
缺点:形态过于简单,且过于知名,容易被特判解决;有些错误算法在菊花上非常快。

具有“扫把”,“蒲公英”等多种俗名的一棵树

优点:兼具链和菊花的特点,且不容易被特判掉。

完全二叉树

优点:容易实现;可以卡重链剖分的常数。
缺点:和“随机树”类似。

根号叉树

\(\sqrt n\) 条长为 \(\sqrt n\) 的链的某一端合并成一个点形成的树。
优点:容易实现;可以卡很多奇怪暴力,而且有些能过链、菊花甚至蒲公英的优秀暴力不一定能轻松过这个。
缺点:形态简单,容易被针对。


上面是一些比较常见的操作,接下来是一些奇技淫巧。

比较高的“随机树”

\(i\) 的父节点为 \([max(1,i-10),i)\) 中的随机数。
这样生成器仍然非常容易实现,且解决了树高问题,结构也不简单,很多情况下已经是很好的数据了。

“毛毛虫”

在链上的每一个点上挂一个叶子作为其儿子。
看似简单,实则非常阴间,卡暴力时实际效果良好。

缝合怪

生成几棵各具特色的树,把它们连在一起。比较简单的用法是在你精心构造的树上面随机挂几个叶子。
很多时候能保有原树的特色,但也不是万能的,比如不能卡树剖。

二项树

\(T(n)\) 是第 \(n\) 棵二项树,则 \(T(n)\) 由一个根节点,和 \(T(n-1),T(n-2)\) 作为其两棵子树构成。
具有完全二叉树的很多特点,但是两棵子树大小没有那么均匀,可以卡一些奇奇怪怪的东西。下次我去翻一下 u 群消息记录找到我记忆里的那个例子。

块状树

生成一个 \(\sqrt n\) 个点的树,然后将 \(\sqrt n\) 个点都变成长为 \(\sqrt n\) 的链。
这样既能保留一开始生成的树的某些特点,又能在一定程度上保证深度和、子树大小和不太小。

卡长剖

将长为 \(3,5,7,9\ldots\) 的链连在一起,然后挂一堆叶子。能把长剖跳轻边的方法卡到 \(O(\sqrt n)\)
但实际上这棵树从来没卡掉过任何东西。


后记

在绞尽脑汁地造了若干棵形态各异的树,认为自己已经卡掉了非常多暴力的出题人心满意足地把这些树捆进了 subtask 里,然后放心地离开了。
第二天,他得知自己出的题被选手利用点权随机写了一个暴力通过了/px

posted @ 2022-10-11 17:28  秋叶冬雪  阅读(2437)  评论(3编辑  收藏  举报