#P1134. 第2题-塔子的有根树
          
                        
                                    
                      
        
              - 
          
          
                      1500ms
            
          
                      Tried: 266
            Accepted: 70
            Difficulty: 7
            
          
          
          
                       所属公司 : 
                              百度
                                
            
                        
              时间 :2023年3月28日
                              
                      
          
 
- 
                        算法标签>树          
 
第2题-塔子的有根树
思路:懒标记 + dfs
1.末尾的0
某个数x的末尾0的个数 等价于 x 质因分解后2,5 的个数中的较小值。所以我们去记录每个节点的权值中2的个数以及5的个数即可。
例如:25000=23∗55 , 所以它末尾有min(3,5)=3 个0
123=3∗41 , 它里面没有2,5 ,所以末尾没有0
2.懒标记技巧
 每次操作,我们都暴力的dfs整个子树,这个复杂度显然是不够的。根据数据范围我们希望有一个O(1)的做法。不难发现我们可以先只在被操作的节点上加上2,5个数。然后所有操作做完之后,我们使用一遍dfs,自顶向下的将这些贡献往下传递累加。这样的技巧又称为懒标记技巧。
3.最后
 题目让求的是以节点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 = " ")
c++
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10;
int twos[N], fives[N];
vector<int> e[N];
int t, n;
int in[N], Size[N], idx;
int dt[N], df[N];
void dfs(int u, int fa) {
    in[u] = ++idx;
    Size[u] = 1;
    for (auto v : e[u]) {
        if (v == fa) continue;
        dfs(v, u);
        Size[u] += Size[v];
    }
}
void sdfs(int u, int fa) {
    for (auto v : e[u]) {
        if (v == fa) continue;
        sdfs(v, u);
        dt[in[u]] += dt[in[v]];
        df[in[u]] += df[in[v]];
    }
}
void add(int l, int r, int x, int *a) {
    a[l] += x;
    a[r + 1] -= x;
}
signed main() {
    cin >> n;
    for (int i = 1, x; i <= n; i++) {
        cin >> x;
        int two = 0, five = 0;
        while (x % 2 == 0) {
            two++;
            x /= 2;
        }
        while (x % 5 == 0) {
            x /= 5;
            five++;
        }
        twos[i] = two, fives[i] = five;
    }
    for (int i = 1; i <= n - 1; i++) {
        int a, b;
        cin >> a >> b;
        e[a].push_back(b);
        e[b].push_back(a);
    }
    dfs(1, 0);
    // cout << Size[1] << endl;
    for (int i = 1; i <= n; i++) {
        add(in[i], in[i], twos[i], dt);
        add(in[i], in[i], fives[i], df);
    }
    cin >> t;
    while (t--) {
        int u, x;
        cin >> u >> x;
        int two = 0, five = 0;
        while (x % 2 == 0) {
            two++;
            x /= 2;
        }
        while (x % 5 == 0) {
            x /= 5;
            five++;
        }
        add(in[u], in[u] + Size[u] - 1, two, dt);
        add(in[u], in[u] + Size[u] - 1, five, df);
    }
    // sdfs(1, 0);
    for (int i = 1; i <= n; i++) {
        dt[i] += dt[i - 1];
        df[i] += df[i - 1];
    }
    sdfs(1, 0);
    for (int i = 1; i <= n; i++) {
        cout << min(dt[in[i]], df[in[i]]) << " ";
    }
    cout << endl;
}
java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
    static int[][] vals;//【0】为2的个数,【1】为5的个数
    static List<Integer>[] edges;//边表
    static int[][] lazy;//懒加载
    static boolean[] iscan;//记录节点是否访问过
    public static void main(String[] args) throws FileNotFoundException {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        vals = new int[N+1][2];
        for (int i = 1; i <= N; i++) {
            int num = sc.nextInt();
            vals[i][0] = getN(num,2);
            vals[i][1] = getN(num,5);
        }
        lazy = new int[N+1][2];
        edges = new List[N+1];
        iscan = new boolean[N+1];
        for (int i = 1; i < edges.length; i++) {
            edges[i] = new ArrayList<>();
        }
        for (int i = 0; i < N - 1; i++) {
            int from = sc.nextInt();
            int to = sc.nextInt();
            edges[from].add(to);
            edges[to].add(from);
        }
        int Q = sc.nextInt();
        for (int i = 0; i < Q; i++) {
            int node = sc.nextInt();
            int mulNum = sc.nextInt();
            lazy[node][0] += getN(mulNum,2);
            lazy[node][1] += getN(mulNum,5);
        }
        dfs1(1);
        clear(iscan);
        dfs2(1);
        for (int i = 1; i <=N ; i++) {
            System.out.print(Math.min(vals[i][0],vals[i][1])+" ");
        }
    }
    private static void dfs2(int root) {//自低向上进行求和
        List<Integer> edge = edges[root];
        iscan[root] = true;
        for (Integer integer : edge) {
            if (!iscan[integer]){
                dfs2(integer);
                vals[root][0]+=vals[integer][0];
                vals[root][1]+=vals[integer][1];
            }
        }
    }
    private static void dfs1(int root) {//自顶向下处理lazy数组
        int i2 = lazy[root][0];
        int i5 = lazy[root][1];
        vals[root][0]+=i2;
        vals[root][1]+=i5;
        iscan[root] = true;
        List<Integer> edge = edges[root];
        for (Integer integer : edge) {
            if (!iscan[integer]){
                lazy[integer][0]+=i2;
                lazy[integer][1]+=i5;
                dfs1(integer);
            }
        }
    }
    private static int getN(int a, int b ) {
        int ans = 0;
        while ((a%b) == 0){
            ans++;
            a/=b;
        }
        return ans;
    }
    private static void clear(boolean[] iscan) {//重置iscan数组
        for (int i = 0; i < iscan.length; i++) {
            iscan[i] = false;
        }
    }
}
        题目内容
在一个偏远的山区里,有一个叫做小红的数学家。他热爱数学,对树这种数据结构也有着浓厚的兴趣。
有一天,他在森林中漫步时发现了一棵美丽的大树。这棵树非常漂亮,每个节点 i 都是独特的,有着它自己的权值 vali ,并且规定 1 号点为这棵树的根节点。
小红对这棵树产生了浓厚的兴趣,他开始研究这棵树的性质,并思考一些问题。他想知道如果对于树上的某个节点 t ,以 t 为根的子树中所有节点的权值都乘上一个 g ,会对整棵树产生什么影响。
为了更好地研究这个问题,他进行了 q 次操作,每次选择了一个节点 t ,并将以 t 为根的子树中所有节点的权值都乘上了一个 g 。这个过程中,他记录了每个节点的最终权值,但他还想知道一个更有趣的问题:在 q 次操作结束以后,以节点 i 为根的子树中所有节点的权值的乘积的末尾有多少个0。
这个问题非常有趣,因为它不仅涉及到树的结构,还需要考虑数学中数字的性质。小红非常期待你能帮他解决这个问题。
输入描述
第一行输入一个正整数n,代表这颗树的节点的数量。
第二行输入n个正整数vali,代表每个节点的权值。
接下来的n−1行,每行输入两个正整数u和v,代表节点u和节点v有一条边相连。
接下来的一行输入一个正整数q,代表操作次数。
接下来的q行,每行输入两个正整数t和g,代表小红的一次操作。
1⩽n,q⩽105
1⩽vi,g⩽109
1⩽t,u,v⩽n
输出描述
输出一行n个正整数,分别代表1号节点到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