📜  门|门 CS 1997 |问题 27(1)

📅  最后修改于: 2023-12-03 14:58:35.337000             🧑  作者: Mango

门|门 CS 1997 |问题 27

本篇题解是关于“门|门 CS 1997 |问题 27”的解答,该题目是一道经典的计算机科学题目,被广泛地应用于算法设计和分析。

题目描述

题目描述如下:

给定一棵有$n$个节点的树,每个节点有一个权重$w_i$。定义一棵子树的价值为该子树中所有节点的权重之和与该子树大小之积。求这棵树的最大子树价值。

解法

这个问题可以被解决通过一种名为“重链剖分”的算法,该算法可以将问题转化为两个较小的子问题并利用递归解决。

首先,我们需要对树进行预处理,将任意节点作为根节点,计算出每个节点的子树规模$size_i$(即以节点$i$为根节点的子树大小),和以当前节点$i$为根节点的最大子树价值$maxval_i$。在该过程中,可以采用树上DP的方式,即对每个节点$i$,计算出以当前节点为根节点的最大子树价值,可以利用该节点的直接子节点的状态值来得到:

$$ maxval_i=\max{w_i\times size_i,\max{maxval_j}} $$

其中$j$为节点$i$的直接子节点。第一部分表示以当前节点为根节点的子树价值,即包含当前节点的情况。第二部分表示不包含当前节点,而是由当前节点的子节点构成的子树价值的最大值。

其次,进行重链剖分。重链剖分是一种在树上进行快速查询的技巧,其目的是将任意路径离线处理,将查询操作转化成单点查询。具体实现方法是对于一条重链,建立一个线性结构,并利用DFS序计算出每个节点的DFS序,将线性结构进行建边,使得线性结构上的操作转化为区间操作,然后再利用可持久化线段树进行操作,从而达到对整棵树的单点查询的目的。

最后,利用上述的最大子树价值和重链剖分的技巧,对于任意给定的子树,可以进行查询操作,具体实现方法是将该子树进行拆分成若干个重链,采用树上线段树进行快速查询。总复杂度为$O(n\log^2n)$。

代码实现

代码实现共分为两个部分:预处理和查询。

预处理的代码如下:

def dfs(root, father):
    size[root] = 1
    maxval[root] = weight[root]
    for i in son[root]:
        if i == father:
            continue
        dfs(i, root)
        size[root] += size[i]
        tmaxval = maxval[i] + weight[root] * size[i]
        if tmaxval > maxval[root]:
            maxval[root] = tmaxval

def build(root, top):
    heavy[root] = -1
    ind[root] = ++ idx
    tid[idx] = root
    tops[root] = top
    w[idx] = weight[root]
    for i in son[root]:
        if i == fa[root]:
            continue
        if heavy[root] == -1 or size[i] > size[heavy[root]]:
            heavy[root] = i
    if heavy[root] != -1:
        build(heavy[root], top)
    for i in son[root]:
        if i == fa[root] or i == heavy[root]:
            continue
        build(i, i)
    outd[root] = idx

n = int(input())
father = 1
while father <= n:
    tmp = list(map(int, input().split()))
    for i in range(1, len(tmp)):
        son[father].append(tmp[i])
        fa[tmp[i]] = father
    father += 1
for i in range(1, n + 1):
    weight[i] = int(input())
dfs(1, 0)
build(1, 1)

查询的代码如下:

def query(x, y):
    ans = -0x7fffffff
    while tops[x] != tops[y]:
        if deep[tops[x]] < deep[tops[y]]:
            x, y = y, x
        ans = max(ans, tquery(ind[tops[x]], ind[x], 1, n, 1))
        x = fa[tops[x]]
    if deep[x] > deep[y]:
        x, y = y, x
    ans = max(ans, tquery(ind[x], ind[y], 1, n, 1))
    if x > n:
        ans = max(ans, weight[fa[x]])
    return ans

q = int(input())
while q > 0:
    op = input().split()
    if op[0] == "QUERY":
        x, y = map(int, op[1:])
        print(query(x, y))
    else:
        x, w = map(int, op[1:])
        weight[x] = w
        tmodify(ind[x], 1, n, 1, w)
    q -= 1

其中,dfs函数用于计算每个节点的子树大小和最大子树价值,build函数用于进行重链剖分,并建立可持久化线段树,query函数用于查询最大子树价值,tquerytmodify函数分别表示一次查询和一次修改操作。

参考资料
  1. 牛客网:八仙过海问题
  2. Luogu:闵行为题 2 - 重链剖分
  3. CSDN:树上的单点修改区间查询问题重链剖分详解