【备战秋招】每日一题:2023.03.28-百度(第三题)-塔子的有根树

为了更好的阅读体检,可以查看我的算法学习网
本题在线评测链接: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 n1行,每行输入两个正整数 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 1n,q105

1 ⩽ v i , g ⩽ 1 0 9 1 \leqslant v_i,g \leqslant 10^9 1vi,g109

1 ⩽ t , u , v ⩽ n 1 \leqslant t,u,v \leqslant n 1t,u,vn

输出描述

输出一行 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=2355 , 所以它末尾有 m i n ( 3 , 5 ) = 3 min(3,5)=3 min(3,5)=3 个0

123 = 3 ∗ 41 123 = 3 * 41 123=341 , 它里面没有 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 = " ")
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值