三色树
这道题的主要目的是普及无标号无根树的计数方法,故不讨论非多项式时间做法。其实 n n n 完全可以出得更大,但是没必要。
这道题改自 ProjectEuler#677。
思路要点
首先考虑无标号有根树,我们只需维护 dp 数组 R ( n ) , B ( n ) , Y ( n ) R(n), B(n), Y(n) R(n),B(n),Y(n) 即可。通过更新 dp 数组 Q ( d , n ) Q(d, n) Q(d,n) 表示此时不在意节点的根的颜色,且根的度数为 d d d 即可维护红根和蓝根, W ( d , n ) W(d, n) W(d,n) 用于维护孩子没有黄根树的,这可以帮助计算黄根。每次 update 会消耗 Θ ( n ) \Theta(n) Θ(n) 的时间,比如说已经计算完了 R ( n ) R(n) R(n) 的值,那么它作为子树有可能出现了 k k k 次,则选出 ( R ( n ) + k − 1 k − 1 ) \displaystyle \binom{R(n) + k - 1}{k - 1} (k−1R(n)+k−1) 种方案,令 Q ( d , N ) Q(d,N) Q(d,N) 加上 ( R ( n ) + k − 1 k − 1 ) Q ( d − k , N − k n ) \binom{R(n) + k - 1}{k - 1} Q(d - k, N - kn) (k−1R(n)+k−1)Q(d−k,N−kn)。
这一部分的复杂度为 Θ ( n 2 ) \Theta(n^2) Θ(n2)。
计算无标号无根树的要点是观察树的一个特殊点:一颗树要么存在唯一一个重心使得任何一个子树的大小均 < n 2 < \frac{n}2 <2n,要么存在一个“重边”使得它将树恰好分为大小为 n 2 \frac n2 2n 的两部分。
对于前者部分的计数,我们只需将 dp 数组计算到 ⌈ n 2 ⌉ − 1 \lceil \frac n2 \rceil - 1 ⌈2n⌉−1 时,将 Q , R Q,R Q,R 数组取出。对于后者,若 n n n 是偶数,当计算完 n 2 \frac n2 2n 的 dp 值时,将 R , B , Y R, B, Y R,B,Y 用于计算即可。
如何做到更快
考虑计算 R , B , Y R,B,Y R,B,Y 的生成函数 G R ( x ) , G B ( x ) , G Y ( x ) G_R(x), G_B(x), G_Y(x) GR(x),GB(x),GY(x)。
我们记
S
=
G
R
+
G
B
+
G
Y
S = G_R + G_B + G_Y
S=GR+GB+GY,我们使用 Pólya 计数定理表示
3
3
3 个孩子的树,通过枚举置换群:
S
(
x
)
3
+
3
S
(
x
2
)
S
(
x
)
+
2
S
(
x
3
)
3
!
\frac{S(x)^3 + 3S(x^2)S(x) + 2S(x^3)}{3!}
3!S(x)3+3S(x2)S(x)+2S(x3)
读者可自己尝试推导 4 4 4 和 2 2 2 个孩子的树。
由此我们可以通过 G R , G B , G Y G_R, G_B, G_Y GR,GB,GY 自身带入 x , x 2 , x 3 x, x^2, x^3 x,x2,x3 这些的多项式来表示。接下来考虑如何求解。
我们让 G R , G B , G Y G_R, G_B, G_Y GR,GB,GY 放在一起进行迭代,我们考虑倍增,由于已经计算出了前 n 2 \frac n2 2n 项,所有带入 x 2 , x 3 x^2, x^3 x2,x3 的部分就已经确定了,我们可以认为是常数。接下来就是一个关于 G R , G B , G Y G_R, G_B, G_Y GR,GB,GY 的三元三次方程,这依然可以通过牛顿迭代解决,也就是这等价于根据 G R , G B , G Y G_R, G_B, G_Y GR,GB,GY 的定义式得到了 3 3 3 个它们之间的关系式 E R / E B / E Y ( G R , G B , G Y ) = 0 E_R/E_B/E_Y(G_R, G_B, G_Y) = 0 ER/EB/EY(GR,GB,GY)=0,将 E R / E B / E Y ( u , v , w ) E_R/E_B/E_Y(u, v, w) ER/EB/EY(u,v,w) 在 G R , G B , G Y G_R, G_B, G_Y GR,GB,GY 处泰勒展开就是一次方程,求解一个三元一次方程即可。
本算法的复杂度为
Θ
(
n
log
n
)
\Theta(n\log n)
Θ(nlogn),常数显而易见的大,欢迎有兴趣的同学来实现。
押韵
事实上本题是 UOJ #450. 【集训队作业2018】复读机 的一个加强版。
算法一
n ≤ 5 × 1 0 4 n\le 5\times 10^4 n≤5×104,我可以做多项式快速幂!这个多项式就是 ( ∑ j ≥ 0 x j d ( j d ) ! ) k (\sum_{j\ge 0} \frac{x^{jd}}{(jd)!})^k (∑j≥0(jd)!xjd)k。这个模数不太友好,你可能需要 MTT……常数应该很大。
时间复杂度 Θ ( n log n ) ∼ Θ ( n log n log k ) \Theta(n\log n) \sim \Theta(n\log n \log k) Θ(nlogn)∼Θ(nlognlogk),前者是进行多项式 ln , exp \ln , \exp ln,exp 达到的复杂度,但是常数可能很大。
预计得分 20 % 20\% 20%。
算法二
我们考察 ( ∑ j ≥ 0 x j d ( j d ) ! ) k (\sum_{j\ge 0} \frac{x^{jd}}{(jd)!})^k (∑j≥0(jd)!xjd)k 可以如何表示。
事实上与周期函数有关的,我们可以自然想到类似 DFT 的形式,也就是考虑到 ∑ j = 0 d − 1 ω d c j = d [ d ∣ c ] \sum_{j=0}^{d - 1} \omega_d^{cj} = d[d | c] ∑j=0d−1ωdcj=d[d∣c],因此我们可以知道上面的那个多项式实际上是
1 d ∑ j = 0 d − 1 exp ω d j x \frac1d \sum_{j=0}^{d-1} \exp \omega_d^j x d1j=0∑d−1expωdjx
我们直接枚举拆括号的过程中计算了几个 exp ω d 0 x \exp \omega_d^0 x expωd0x,几个 exp ω d 1 x \exp \omega_d^1 x expωd1x……然后这一部分的贡献就是 ( c 0 + c 1 ω d 1 + c 2 ω d 2 + … ) n (c_0 + c_1 \omega_d^1 + c_2 \omega_d^2 + \dots)^n (c0+c1ωd1+c2ωd2+…)n。最后总和除以 d k d^k dk。
时间复杂度 Θ ( k d − 1 log n ) \Theta(k^{d-1}\log n) Θ(kd−1logn),预计得分 50 % 50\% 50%。
算法三
主要思想还是围绕优化计算下式:
[ x n ] ( 1 d ∑ j = 0 d − 1 exp ω d j x ) k [x^n]\left( \frac1d \sum_{j=0}^{d-1} \exp \omega_d^j x \right)^k [xn](d1j=0∑d−1expωdjx)k
对于 d = 4 d=4 d=4 的情况,我们实际上是只需要统计每个 exp ( a + b i ) x \exp (a + b\mathrm{i})x exp(a+bi)x 出现多少次。通过推导一些式子,或者直接将复平面旋转 4 5 ∘ 45^\circ 45∘ 我们可以得到,方案数是 ( k k + ∣ a + b ∣ 2 ) ( k k + ∣ a − b ∣ 2 ) \displaystyle\binom{k}{\frac{k + |a+b|}2} \binom{k}{\frac{k + |a-b|}2} (2k+∣a+b∣k)(2k+∣a−b∣k)。因此答案可以在 Θ ( k 2 log n ) \Theta(k^2 \log n) Θ(k2logn) 内计算出来。
预计得分 70 % 70\% 70%。
算法四
对于
d
=
6
d=6
d=6 的情况,我们注意到
ω
6
\omega_6
ω6 的代数关系有
ω
−
1
=
ω
2
\omega - 1 = \omega^2
ω−1=ω2,因此所有的根都可以用
1
,
ω
1, \omega
1,ω 表示,即给每个数赋予了一个离散的二维坐标
ω
0
=
1
ω
1
=
ω
ω
2
=
−
1
+
ω
ω
3
=
−
1
ω
4
=
−
ω
ω
5
=
1
+
−
ω
\begin{array}{clclc} \omega^0 & = & 1 & & \\ \omega^1 & = & & & \omega\\ \omega^2 & = & -1 & + & \omega\\ \omega^3 & = & -1 & & \\ \omega^4 & = & & &-\omega\\ \omega^5 & = & 1 & + &-\omega \end{array}
ω0ω1ω2ω3ω4ω5======1−1−11++ωω−ω−ω
因此我们只需要做一个二维的快速幂,倍增 FFT 可以做到
Θ
(
k
2
log
k
)
\Theta(k^2 \log k)
Θ(k2logk)。但是常数可能较大。
预计得分 80 % ∼ 100 % 80\% \sim 100\% 80%∼100%。
算法五
考虑计算一个二元母函数的高阶幂, G ( x , y ) = F ( x , y ) k G(x, y) = F(x, y)^k G(x,y)=F(x,y)k,可以得到 F ∂ G ∂ x = k G ∂ F ∂ x F \frac{\partial G}{\partial_x} = kG \frac{\partial F}{\partial_x} F∂x∂G=kG∂x∂F,由此可列得递推式子解出所有系数。计算高阶幂的复杂度是 Θ ( k 2 ) \Theta(k^2) Θ(k2) 的。复杂度 Θ ( k 2 log n ) \Theta(k^2 \log n) Θ(k2logn) 且常数较小。
事实上这也是可以直接用来做 k = 4 k=4 k=4 的情况的。
预计得分 100 % 100\% 100%。
further
对于更大的 d d d,我们期望能得到一个怎样的做法?考虑 d d d 次单位根的代数关系,我们希望能够基于一个它们的有理系数线性组合的基。由此则能将维度缩到基的维度,在其上进行快速幂。
我将说明维度可以达到
φ
(
d
)
\varphi(d)
φ(d),而数学迷告诉我说通过一些关于域的理论可以证明这也是下界,等我学了之后补一下证明。
考虑分圆多项式 Φ d ( x ) = ∏ 0 ≤ k < d , gcd ( d , k ) = 1 ( x − ω d k ) \Phi_d(x) = \prod_{0\le k < d, \gcd(d, k) = 1} (x - \omega_d^k) Φd(x)=∏0≤k<d,gcd(d,k)=1(x−ωdk),它的次数显然是 φ ( d ) \varphi(d) φ(d)。
显然分圆多项式也可以用容斥原理重写,即
Φ d ( x ) = ∏ k ∣ d ( x d / k − 1 ) μ ( k ) \Phi_d(x) = \prod_{k|d} (x^{d/k}-1)^{\mu(k)} Φd(x)=k∣d∏(xd/k−1)μ(k)
显然该多项式的系数都是整数。由于 Φ d ( ω d ) = 0 \Phi_d(\omega_d) = 0 Φd(ωd)=0,不难得到 ω d k = x k m o d Φ d ∣ x = ω \omega_d^k = \left.x^k \bmod \Phi_d\right \vert_{x=\omega} ωdk=xkmodΦd∣∣x=ω。因此这个多项式的取模自然而然地导出了每个单位根到 1 , ω d , … , ω d φ ( d ) − 1 1, \omega_d, \dots, \omega_d^{\varphi(d) - 1} 1,ωd,…,ωdφ(d)−1 的线性组合。
观察星象
算法一
显然所选的圆上要么有两个点要么有至少三个。因此分别枚举,枚举两个点作为圆的直径,此外枚举三个点之后可以确定唯一一个外接圆。
时间复杂度 Θ ( n 4 ) \Theta(n^4) Θ(n4),预计得分 10 % 10\% 10%。
算法二
m = n m=n m=n 的情况就是经典的最小圆覆盖问题。
首先将点的顺序打乱,考虑维护前 k k k 个点的最小圆。可以证明前 k + 1 k+1 k+1 个点的最小覆盖圆由前 k k k 个点最小覆盖圆上的关键点和第 k + 1 k+1 k+1 个点,新的最小覆盖圆的关键点一定在这些点之间。因此只有常数种情况,每种通过 Θ ( n ) \Theta(n) Θ(n) 的 check 即可。第 k k k 个点不在前 k − 1 k-1 k−1 个点的最小覆盖圆内的概率显然是 O ( 1 k ) O(\frac1k) O(k1),因此第 k k k 个点消耗的期望复杂度是 Θ ( k ) ⋅ O ( 1 k ) = Θ ( 1 ) \Theta(k) \cdot O(\frac1k) = \Theta(1) Θ(k)⋅O(k1)=Θ(1)。
期望时间复杂度 Θ ( n ) \Theta(n) Θ(n),预计得分 20 % 20\% 20%。
算法三
我们考虑二分答案。那么接下来枚举哪个点在圆上,其它每个点我们可以计算出它对于当前圆在哪个夹角区间内,进行一遍前缀和即可查找出是否存在一个夹角使得该圆包含 m m m 个点。
时间复杂度 Θ ( n 2 log n log x ϵ ) \Theta(n^2\log n \log \frac{x}{\epsilon}) Θ(n2lognlogϵx),预计得分 60 % 60\% 60%。
算法四
我们对算法三进行进一步的观察。事实上我们可以看成这样一个问题,对于第 i i i 个点,客观上存在一个 r i r_i ri 即最小的圆的半径使得圆内有 m m m 个点(我们认为无解即 + ∞ +\infty +∞),我们在二分的过程中,总共会预计调用 Θ ( n log x ϵ ) \Theta(n\log \frac{x}{\epsilon}) Θ(nlogϵx) 次检查。这实际上是有所浪费的,在同样的检查次数内,实际上我们还可以算出每个 i i i 对应的 r i r_i ri,换句话说我们多获取了不必要的信息,因为我们只关心最小值。
接下来就是有趣的地方了:我们先将序列随机打乱,并把当前最小值设为 a = + ∞ a = +\infty a=+∞。接下来我们将点一个个考虑,我们实际上可以一次检查出当前点的答案是否可以 < a < a <a。如果并不小于,我们就可以略过它,否则对该点的答案进行二分。这样显然是进行 n + L log x ϵ n + L\log \frac{x}{\epsilon} n+Llogϵx 次检查,其中 L L L 是 r i r_i ri 序列的单调栈长度。
显然最坏情况是 r r r 互不相同时,单调栈长度的期望最长,这等价于一个排列的期望长度。而这一期望长度等于 H n = ∑ k = 1 n 1 k = Θ ( log n ) H_n = \sum_{k=1}^n \frac 1k = \Theta(\log n) Hn=∑k=1nk1=Θ(logn)。
因此本算法的期望复杂度为 Θ ( ( n + log n log x ϵ ) n log n ) \Theta( (n + \log n \log \frac{x}{\epsilon}) n\log n) Θ((n+lognlogϵx)nlogn),预计得分 100 % 100\% 100%。
本文探讨了无标号无根树的计数方法,重点介绍了一种基于多项式迭代的高效算法,复杂度达到Θ(nlogn)。文中详细解释了如何利用生成函数和Pólya计数定理优化计算过程,以及如何通过牛顿迭代解决三元三次方程。此外,还提供了多种算法策略,包括多项式快速幂、基于周期函数的表示、针对特定d值的优化方法等。
800

被折叠的 条评论
为什么被折叠?



