为了更好的阅读体检,可以查看我的算法学习网
本题在线评测链接:P1134
题目内容
在一个偏远的山区里,有一个叫做塔子哥的数学家。他热爱数学,对树这种数据结构也有着浓厚的兴趣。
有一天,他在森林中漫步时发现了一棵美丽的大树。这棵树非常漂亮,每个节点 i i i 都是独特的,有着它自己的权值 v a l i val_i vali ,并且规定 1 1 1 号点为这棵树的根节点。
塔子哥对这棵树产生了浓厚的兴趣,他开始研究这棵树的性质,并思考一些问题。他想知道如果对于树上的某个节点 t t t ,以 t t t 为根的子树中所有节点的权值都乘上一个 g g g ,会对整棵树产生什么影响。
为了更好地研究这个问题,他进行了 q q q 次操作,每次选择了一个节点 t t t ,并将以 t t t 为根的子树中所有节点的权值都乘上了一个 g g g 。这个过程中,他记录了每个节点的最终权值,但他还想知道一个更有趣的问题:在 q q q 次操作结束以后,以节点 i i i 为根的子树中所有节点的权值的乘积的末尾有多少个0。
这个问题非常有趣,因为它不仅涉及到树的结构,还需要考虑数学中数字的性质。塔子哥非常期待你能帮他解决这个问题。
输入描述
第一行输入一个正整数 n n n,代表这颗树的节点的数量。
第二行输入 n n n个正整数 v a l i val_i vali,代表每个节点的权值。
接下来的 n − 1 n - 1 n−1行,每行输入两个正整数 u u u和 v v v,代表节点 u u u和节点 v v v有一条边相连。
接下来的一行输入一个正整数 q q q,代表操作次数。
接下来的 q q q行,每行输入两个正整数 t t t和 g g g,代表塔子哥的一次操作。
1 ⩽ n , q ⩽ 1 0 5 1 \leqslant n,q \leqslant 10^5 1⩽n,q⩽105
1 ⩽ v i , g ⩽ 1 0 9 1 \leqslant v_i,g \leqslant 10^9 1⩽vi,g⩽109
1 ⩽ t , u , v ⩽ n 1 \leqslant t,u,v \leqslant n 1⩽t,u,v⩽n
输出描述
输出一行 n n n个正整数,分别代表1号节点到 n n n号节点,每个节点的子树权值乘积尾零的数量。
样例
输入
6
1 2 3 4 5 6
1 2
2 3
1 4
2 5
4 6
2
2 5
4 5
输出
4 1 0 2 0 1
思路:懒标记 + dfs
1.末尾的0
某个数 x x x的末尾0的个数 等价于 x x x 质因分解后 2 , 5 2,5 2,5 的个数中的较小值。所以我们去记录每个节点的权值中2的个数以及5的个数即可。
例如: 25000 = 2 3 ∗ 5 5 25000 = 2^3 * 5^5 25000=23∗55 , 所以它末尾有 m i n ( 3 , 5 ) = 3 min(3,5)=3 min(3,5)=3 个0
123 = 3 ∗ 41 123 = 3 * 41 123=3∗41 , 它里面没有 2 , 5 2,5 2,5 ,所以末尾没有0
2.懒标记技巧
每次操作,我们都暴力的dfs整个子树,这个复杂度显然是不够的。根据数据范围我们希望有一个 O ( 1 ) O(1) O(1)的做法。不难发现我们可以先只在被操作的节点上加上2,5个数。然后所有操作做完之后,我们使用一遍dfs,自顶向下的将这些贡献往下传递累加。这样的技巧又称为懒标记技巧。
3.最后
题目让求的是以节点 i i i为根的子树的所有点的权值的乘积 . 所以最后还需要一遍自底向上的dfs,求一个子树和。
代码
python
from collections import defaultdict
# 计算x中有多少个y
def get(x , y):
cnt = 0
while x % y == 0:
cnt += 1
x //= y
return cnt
# e 是邻接矩阵
e = defaultdict(list)
# lazy 是懒标记
lazy = defaultdict(list)
# 读入
n = int(input())
a = [0] + list(map(int , input().split()))
for i in range (n - 1):
u , v = list(map(int , input().split()))
e[u].append(v)
e[v].append(u)
for i in range (1 , n + 1):
lazy[i].append(0)
lazy[i].append(0)
q = int(input())
for i in range (q):
t , g = list(map(int , input().split()))
x = get(g , 2)
y = get(g , 5)
# 先在被操作的点上打上标记
lazy[t][0] += x
lazy[t][1] += y
res = []
for i in range (n + 1):
res.append([0 , 0])
# 第一遍dfs,自顶向下的将lazy的贡献往下传递
def dfs1(u , fa):
for v in e[u]:
if v == fa:
continue
lazy[v][0] += lazy[u][0]
lazy[v][1] += lazy[u][1]
dfs1(v , u)
return
# 第二遍dfs,自底向上的求子树的lazy和
def dfs2(u , fa):
for v in e[u]:
if v == fa:
continue
dfs2(v , u)
lazy[u][0] += lazy[v][0]
lazy[u][1] += lazy[v][1]
res[u] = lazy[u][:]
return
dfs1(1 , -1)
# 记得把本身也加上
for i in range (1 , n + 1):
lazy[i][0] += get(a[i] , 2)
lazy[i][1] += get(a[i] , 5)
dfs2(1 , -1)
# 输出
for i in range (1 , n + 1):
print (min(res[i][0] , res[i][1]) , end = " ")