三色树
这道题的主要目的是普及无标号无根树的计数方法,故不讨论非多项式时间做法。其实
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 − 1 R ( 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 − 1 R ( n ) + k − 1 ) Q ( d − k , N − k n ) 。
这一部分的复杂度为
Θ
(
n
2
)
\Theta(n^2)
Θ ( n 2 ) 。
计算无标号无根树的要点是观察树的一个特殊点:一颗树要么存在唯一一个重心使得任何一个子树的大小均
<
n
2
< \frac{n}2
< 2 n ,要么存在一个“重边”使得它将树恰好分为大小为
n
2
\frac n2
2 n 的两部分。
对于前者部分的计数,我们只需将 dp 数组计算到
⌈
n
2
⌉
−
1
\lceil \frac n2 \rceil - 1
⌈ 2 n ⌉ − 1 时,将
Q
,
R
Q,R
Q , R 数组取出。对于后者,若
n
n
n 是偶数,当计算完
n
2
\frac n2
2 n 的 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)
G R ( x ) , G B ( x ) , G Y ( x ) 。
我们记
S
=
G
R
+
G
B
+
G
Y
S = G_R + G_B + G_Y
S = G R + G B + G Y ,我们使用 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 + 3 S ( x 2 ) S ( x ) + 2 S ( x 3 )
读者可自己尝试推导
4
4
4 和
2
2
2 个孩子的树。
由此我们可以通过
G
R
,
G
B
,
G
Y
G_R, G_B, G_Y
G R , G B , G Y 自身带入
x
,
x
2
,
x
3
x, x^2, x^3
x , x 2 , x 3 这些的多项式来表示。接下来考虑如何求解。
我们让
G
R
,
G
B
,
G
Y
G_R, G_B, G_Y
G R , G B , G Y 放在一起进行迭代,我们考虑倍增,由于已经计算出了前
n
2
\frac n2
2 n 项,所有带入
x
2
,
x
3
x^2, x^3
x 2 , x 3 的部分就已经确定了,我们可以认为是常数。接下来就是一个关于
G
R
,
G
B
,
G
Y
G_R, G_B, G_Y
G R , G B , G Y 的三元三次方程,这依然可以通过牛顿迭代解决,也就是这等价于根据
G
R
,
G
B
,
G
Y
G_R, G_B, G_Y
G R , G B , G Y 的定义式得到了
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
E R / E B / E Y ( G R , G B , G Y ) = 0 ,将
E
R
/
E
B
/
E
Y
(
u
,
v
,
w
)
E_R/E_B/E_Y(u, v, w)
E R / E B / E Y ( u , v , w ) 在
G
R
,
G
B
,
G
Y
G_R, G_B, G_Y
G R , G B , G Y 处泰勒展开就是一次方程,求解一个三元一次方程即可。
本算法的复杂度为
Θ
(
n
log
n
)
\Theta(n\log n)
Θ ( n log n ) ,常数显而易见的大,欢迎有兴趣的同学来实现。
押韵
事实上本题是 UOJ #450. 【集训队作业2018】复读机 的一个加强版。
算法一
n
≤
5
×
1
0
4
n\le 5\times 10^4
n ≤ 5 × 1 0 4 ,我可以做多项式快速幂!这个多项式就是
(
∑
j
≥
0
x
j
d
(
j
d
)
!
)
k
(\sum_{j\ge 0} \frac{x^{jd}}{(jd)!})^k
( ∑ j ≥ 0 ( j d ) ! x j d ) k 。这个模数不太友好,你可能需要 MTT……常数应该很大。
时间复杂度
Θ
(
n
log
n
)
∼
Θ
(
n
log
n
log
k
)
\Theta(n\log n) \sim \Theta(n\log n \log k)
Θ ( n log n ) ∼ Θ ( n log n log k ) ,前者是进行多项式
ln
,
exp
\ln , \exp
ln , exp 达到的复杂度,但是常数可能很大。
预计得分
20
%
20\%
2 0 % 。
算法二
我们考察
(
∑
j
≥
0
x
j
d
(
j
d
)
!
)
k
(\sum_{j\ge 0} \frac{x^{jd}}{(jd)!})^k
( ∑ j ≥ 0 ( j d ) ! x j d ) k 可以如何表示。
事实上与周期函数有关的,我们可以自然想到类似 DFT 的形式,也就是考虑到
∑
j
=
0
d
−
1
ω
d
c
j
=
d
[
d
∣
c
]
\sum_{j=0}^{d - 1} \omega_d^{cj} = d[d | c]
∑ j = 0 d − 1 ω d c j = d [ d ∣ c ] ,因此我们可以知道上面的那个多项式实际上是
1
d
∑
j
=
0
d
−
1
exp
ω
d
j
x
\frac1d \sum_{j=0}^{d-1} \exp \omega_d^j x
d 1 j = 0 ∑ d − 1 exp ω d j x
我们直接枚举拆括号的过程中计算了几个
exp
ω
d
0
x
\exp \omega_d^0 x
exp ω d 0 x ,几个
exp
ω
d
1
x
\exp \omega_d^1 x
exp ω d 1 x ……然后这一部分的贡献就是
(
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
( c 0 + c 1 ω d 1 + c 2 ω d 2 + … ) n 。最后总和除以
d
k
d^k
d k 。
时间复杂度
Θ
(
k
d
−
1
log
n
)
\Theta(k^{d-1}\log n)
Θ ( k d − 1 log n ) ,预计得分
50
%
50\%
5 0 % 。
算法三
主要思想还是围绕优化计算下式:
[
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
[ x n ] ( d 1 j = 0 ∑ d − 1 exp ω d j x ) k
对于
d
=
4
d=4
d = 4 的情况,我们实际上是只需要统计每个
exp
(
a
+
b
i
)
x
\exp (a + b\mathrm{i})x
exp ( a + b i ) x 出现多少次。通过推导一些式子,或者直接将复平面旋转
4
5
∘
45^\circ
4 5 ∘ 我们可以得到,方案数是
(
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}
( 2 k + ∣ a + b ∣ k ) ( 2 k + ∣ a − b ∣ k ) 。因此答案可以在
Θ
(
k
2
log
n
)
\Theta(k^2 \log n)
Θ ( k 2 log n ) 内计算出来。
预计得分
70
%
70\%
7 0 % 。
算法四
对于
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 − 1 1 + + ω ω − ω − ω 因此我们只需要做一个二维的快速幂,倍增 FFT 可以做到
Θ
(
k
2
log
k
)
\Theta(k^2 \log k)
Θ ( k 2 log k ) 。但是常数可能较大。
预计得分
80
%
∼
100
%
80\% \sim 100\%
8 0 % ∼ 1 0 0 % 。
算法五
考虑计算一个二元母函数的高阶幂,
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 = k G ∂ x ∂ F ,由此可列得递推式子解出所有系数。计算高阶幂的复杂度是
Θ
(
k
2
)
\Theta(k^2)
Θ ( k 2 ) 的。复杂度
Θ
(
k
2
log
n
)
\Theta(k^2 \log n)
Θ ( k 2 log n ) 且常数较小。
事实上这也是可以直接用来做
k
=
4
k=4
k = 4 的情况的。
预计得分
100
%
100\%
1 0 0 % 。
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 , g cd( d , k ) = 1 ( x − ω d k ) ,它的次数显然是
φ
(
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 ∏ ( x d / 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}
ω d k = x k m o d Φ 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)
Θ ( n 4 ) ,预计得分
10
%
10\%
1 0 % 。
算法二
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 ( k 1 ) ,因此第
k
k
k 个点消耗的期望复杂度是
Θ
(
k
)
⋅
O
(
1
k
)
=
Θ
(
1
)
\Theta(k) \cdot O(\frac1k) = \Theta(1)
Θ ( k ) ⋅ O ( k 1 ) = Θ ( 1 ) 。
期望时间复杂度
Θ
(
n
)
\Theta(n)
Θ ( n ) ,预计得分
20
%
20\%
2 0 % 。
算法三
我们考虑二分答案。那么接下来枚举哪个点在圆上,其它每个点我们可以计算出它对于当前圆在哪个夹角区间内,进行一遍前缀和即可查找出是否存在一个夹角使得该圆包含
m
m
m 个点。
时间复杂度
Θ
(
n
2
log
n
log
x
ϵ
)
\Theta(n^2\log n \log \frac{x}{\epsilon})
Θ ( n 2 log n log ϵ x ) ,预计得分
60
%
60\%
6 0 % 。
算法四
我们对算法三进行进一步的观察。事实上我们可以看成这样一个问题,对于第
i
i
i 个点,客观上存在一个
r
i
r_i
r i 即最小的圆的半径使得圆内有
m
m
m 个点(我们认为无解即
+
∞
+\infty
+ ∞ ),我们在二分的过程中,总共会预计调用
Θ
(
n
log
x
ϵ
)
\Theta(n\log \frac{x}{\epsilon})
Θ ( n log ϵ x ) 次检查。这实际上是有所浪费的,在同样的检查次数内,实际上我们还可以算出每个
i
i
i 对应的
r
i
r_i
r i ,换句话说我们多获取了不必要的信息,因为我们只关心最小值。
接下来就是有趣的地方了:我们先将序列随机打乱,并把当前最小值设为
a
=
+
∞
a = +\infty
a = + ∞ 。接下来我们将点一个个考虑,我们实际上可以一次检查出当前点的答案是否可以
<
a
< a
< a 。如果并不小于,我们就可以略过它,否则对该点的答案进行二分。这样显然是进行
n
+
L
log
x
ϵ
n + L\log \frac{x}{\epsilon}
n + L log ϵ x 次检查,其中
L
L
L 是
r
i
r_i
r i 序列的单调栈长度。
显然最坏情况是
r
r
r 互不相同时,单调栈长度的期望最长,这等价于一个排列的期望长度。而这一期望长度等于
H
n
=
∑
k
=
1
n
1
k
=
Θ
(
log
n
)
H_n = \sum_{k=1}^n \frac 1k = \Theta(\log n)
H n = ∑ k = 1 n k 1 = Θ ( log n ) 。
因此本算法的期望复杂度为
Θ
(
(
n
+
log
n
log
x
ϵ
)
n
log
n
)
\Theta( (n + \log n \log \frac{x}{\epsilon}) n\log n)
Θ ( ( n + log n log ϵ x ) n log n ) ,预计得分
100
%
100\%
1 0 0 % 。