题解:CF1778F Maximizing Root
提供一个自顶向下的做法,不是 DP。加上快读后可以跑到 Rank 1。
定义
定义
定义
思考
- 既然要让答案最大,每次操作选的
x 应当是子树所有数的 GCD。 - 按照从叶子到根的顺序操作,可以让答案尽量大。例如从上到下是
16,4,2 ,先操作上面,只能把16 乘以2 ;先操作下面,最后可以让16 乘以16 。 - 答案一定是
a_1 乘以它的一个因子。由于1000 以内的数至多有32 个因子(例如840 ),所以我们可以从大到小地枚举a_1 的因子d 。如果1 的每个儿子子树都能通过操作(操作次数之和小于k ),使得子树所有数的 GCD 是d 的倍数,那么答案就是a_1\cdot d 。 - 写一个自顶向下的 DFS,额外传入参数
d 。考虑1 的儿子v ,分类讨论:- 如果
\text{subGcd}(v) 已经是d 的倍数,那么无需操作子树v 中的任何节点。 - 否则,如果
\text{subGcd}(v)^2 是d 的倍数,那么只需要在节点v 上操作一次,无需操作更下面的节点。 - 否则,如果
v 是叶子,或者a_v^2 不是d 的倍数,那么无法完成操作。 - 否则,应该先操作
v 的后代,然后操作v 。我们的目标是,对于v 的每个儿子子树w ,通过操作,使得\text{subGcd}(w) 是\text{ceilSqrt}(d) 的倍数,这样就可以让\text{subGcd}(v)^2 是d 的倍数。这个结论是本题的核心结论。根据该结论,继续往下递归v 的儿子w ,额外传入参数\text{ceilSqrt}(d) 。
- 如果
- 累加 DFS 中的操作次数。如果操作次数小于
k (注意不是小于等于,因为最后还要对节点1 操作一次),那么答案就是a_1\cdot d 。
细节:特判
Go 语言代码(下标从
package main
import ("bufio";."fmt";"os")
func gcd(a, b int) int { for a != 0 { a, b = b%a, a }; return b }
func main() {
in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
const mx = 1000
divisors := [mx + 1][]int{}
for i := mx; i > 0; i-- { // 方便后面从大到小遍历因子
for j := i; j <= mx; j += i {
divisors[j] = append(divisors[j], i)
}
}
// ceilSqrt[i]^2 是 i 的倍数
ceilSqrt := [mx + 1]int{}
for i := 1; i <= mx; i++ {
ceilSqrt[i] = 1
x := i
for p := 2; p*p <= x; p++ {
for p2 := p * p; x%p2 == 0; x /= p2 {
ceilSqrt[i] *= p
}
if x%p == 0 {
ceilSqrt[i] *= p
x /= p
}
}
if x > 1 {
ceilSqrt[i] *= x
}
}
var T, n, k int
for Fscan(in, &T); T > 0; T-- {
Fscan(in, &n, &k)
a := make([]int, n)
for i := range a {
Fscan(in, &a[i])
}
g := make([][]int, n)
for i := 1; i < n; i++ {
var v, w int
Fscan(in, &v, &w)
v--
w--
g[v] = append(g[v], w)
g[w] = append(g[w], v)
}
if k == 0 {
Fprintln(out, a[0])
continue
}
subGcd := make([]int, n)
var dfs0 func(int, int)
dfs0 = func(v, fa int) {
subGcd[v] = a[v]
for _, w := range g[v] {
if w != fa {
dfs0(w, v)
subGcd[v] = gcd(subGcd[v], subGcd[w])
}
}
}
dfs0(0, -1)
var cnt int
var dfs func(int, int, int)
dfs = func(v, fa, d int) {
if subGcd[v]%d == 0 {
// 无需操作
return
}
if subGcd[v]*subGcd[v]%d == 0 {
cnt++ // 操作 v
return
}
if len(g[v]) == 1 || a[v]*a[v]%d > 0 {
cnt = 1e9 // 无法操作
return
}
for _, w := range g[v] {
if w != fa {
dfs(w, v, ceilSqrt[d])
}
}
cnt++ // 操作 v
}
for _, d := range divisors[a[0]] {
cnt = 0
for _, v := range g[0] {
dfs(v, 0, d)
}
if cnt < k {
Fprintln(out, a[0]*d)
break
}
}
}
}
时间复杂度:
欢迎关注 B站@灵茶山艾府